[PATCH 01/13] staging: comedi: pcmuio: fix interrupt requests

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

 



Legacy (ISA) interrupts are not sharable so this driver should not
be passing the IRQF_SHARED flag when requesting the interrupts.

This driver supports two board types:
  PCM-UIO48 with one asic (one interrupt source)
  PCM-UIO96 with two asics (two interrupt sources)

The PCM-UIO96 has a jumper that allows the two interrupt sources to
share an interrupt. This is safe for legacy interrupts as long as
the "shared" interrupt is handled by a single driver.

Modify the request_irq() code in this driver to correctly request the
interrupts. For the PCM-UI048 case (one asic) only one request_irq()
is needed. For the PCM-UIO96 (two asics) there are two cases:

  1) interrupt is shared, one request_irq() call
  2) two interrupts, two request_irq() calls

Do the first request_irq() in all cases. Only do the second if the
boardinfo indicates that the board has two asics and the user passed
the 2nd interrupt number.

Pack the two interrupt numbers in dev->irq instead of storing them in
the private data. During the detach of the board, this driver will
free the 2nd irq if needed and the core will free the 1st.

Move the board reset and interrupt request code so it occurs early
in the attach. We can then check dev->irq to see if the subdevice
interrupt support actually needs to be initialized.

Add a call to pcmuio_reset() in the (*detach) to make sure the
interrupts are disabled before freeing the irqs.

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

diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c
index 13f1943..2c491be 100644
--- a/drivers/staging/comedi/drivers/pcmuio.c
+++ b/drivers/staging/comedi/drivers/pcmuio.c
@@ -146,7 +146,6 @@ struct pcmuio_subdev_private {
 
 struct pcmuio_private {
 	struct {
-		unsigned int irq;
 		spinlock_t spinlock;
 	} asics[PCMUIO_MAX_ASICS];
 	struct pcmuio_subdev_private *sprivs;
@@ -397,20 +396,14 @@ static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
 static irqreturn_t pcmuio_interrupt(int irq, void *d)
 {
 	struct comedi_device *dev = d;
-	struct pcmuio_private *devpriv = dev->private;
-	int got1 = 0;
-	int asic;
+	int handled = 0;
 
-	for (asic = 0; asic < PCMUIO_MAX_ASICS; ++asic) {
-		if (irq == devpriv->asics[asic].irq) {
-			/* it is an interrupt for ASIC #asic */
-			if (pcmuio_handle_asic_interrupt(dev, asic))
-				got1++;
-		}
-	}
-	if (!got1)
-		return IRQ_NONE;	/* interrupt from other source */
-	return IRQ_HANDLED;
+	if (irq == (dev->irq & 0xff))
+		handled += pcmuio_handle_asic_interrupt(dev, 0);
+	if (irq == ((dev->irq >> 8) & 0xff))
+		handled += pcmuio_handle_asic_interrupt(dev, 1);
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int pcmuio_start_intr(struct comedi_device *dev,
@@ -599,12 +592,9 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 	struct pcmuio_private *devpriv;
 	struct pcmuio_subdev_private *subpriv;
 	int sdev_no, n_subdevs, asic;
-	unsigned int irq[PCMUIO_MAX_ASICS];
+	unsigned int irq;
 	int ret;
 
-	irq[0] = it->options[1];
-	irq[1] = it->options[2];
-
 	ret = comedi_request_region(dev, it->options[0],
 				    board->num_asics * PCMUIO_ASIC_IOSIZE);
 	if (ret)
@@ -617,6 +607,39 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 	for (asic = 0; asic < PCMUIO_MAX_ASICS; ++asic)
 		spin_lock_init(&devpriv->asics[asic].spinlock);
 
+	pcmuio_reset(dev);
+
+	/* request the 1st irq */
+	irq = it->options[1];
+	if (irq) {
+		ret = request_irq(irq, pcmuio_interrupt, 0,
+				  board->name, dev);
+		if (ret == 0) {
+			/* save the irq for the core to free */
+			dev->irq = irq;
+		}
+	}
+
+	/* request the 2nd irq if needed */
+	if (board->num_asics == 2 && dev->irq) {
+		irq = it->options[2];
+		if (irq && irq != dev->irq) {
+			/* 1st and 2nd irq differ */
+			ret = request_irq(irq, pcmuio_interrupt, 0,
+					  board->name, dev);
+			if (ret == 0) {
+				/* save the irq for (*detach) to free */
+				dev->irq |= (irq << 8);
+			} else {
+				free_irq(dev->irq, dev);
+				dev->irq = 0;
+			}
+		} else {
+			/* 1st and 2nd irq are the same (or 2nd is zero) */
+			dev->irq |= (irq << 8);
+		}
+	}
+
 	n_subdevs = board->num_asics * 2;
 	devpriv->sprivs = kcalloc(n_subdevs, sizeof(*subpriv), GFP_KERNEL);
 	if (!devpriv->sprivs)
@@ -639,7 +662,7 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		s->n_chan = 24;
 
 		/* subdevices 0 and 2 suppport interrupts */
-		if ((sdev_no % 2) == 0) {
+		if ((sdev_no % 2) == 0 && dev->irq) {
 			/* setup the interrupt subdevice */
 			subpriv->intr.asic = sdev_no / 2;
 			dev->read_subdev = s;
@@ -655,37 +678,26 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		spin_lock_init(&subpriv->intr.spinlock);
 	}
 
-	pcmuio_reset(dev);
-
-	for (asic = 0; irq[0] && asic < PCMUIO_MAX_ASICS; ++asic) {
-		if (irq[asic]
-		    && request_irq(irq[asic], pcmuio_interrupt,
-				   IRQF_SHARED, board->name, dev)) {
-			int i;
-			/* unroll the allocated irqs.. */
-			for (i = asic - 1; i >= 0; --i) {
-				free_irq(irq[i], dev);
-				devpriv->asics[i].irq = irq[i] = 0;
-			}
-			irq[asic] = 0;
-		}
-		devpriv->asics[asic].irq = irq[asic];
-	}
-
 	return 0;
 }
 
 static void pcmuio_detach(struct comedi_device *dev)
 {
 	struct pcmuio_private *devpriv = dev->private;
-	int i;
+	unsigned int irq2;
+
+	if (devpriv) {
+		pcmuio_reset(dev);
+
+		/* free the 2nd irq, the core will free the 1st one */
+		irq2 = dev->irq >> 8;
+		dev->irq &= 0xff;
+		if (irq2 && irq2 != dev->irq)
+			free_irq(irq2, dev);
 
-	for (i = 0; i < PCMUIO_MAX_ASICS; ++i) {
-		if (devpriv->asics[i].irq)
-			free_irq(devpriv->asics[i].irq, dev);
+		if (devpriv->sprivs)
+			kfree(devpriv->sprivs);
 	}
-	if (devpriv && devpriv->sprivs)
-		kfree(devpriv->sprivs);
 	comedi_legacy_detach(dev);
 }
 
-- 
1.8.3.2

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-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