From 04426acfb86ad10968340771bb15f6074e12912f Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Thu, 5 Dec 2013 16:54:03 -0700 Subject: [PATCH] staging: comedi: pcmuio: fix interrupt requests 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 three cases: 1) irq is shared, one request_irq() call 2) only one asic has an irq, one request_irq() call 3) two irqs, two request_irq() calls The irq for the first asic (dev->irq) will be requested during the attach if required. The comedi core will handle the freeing of this irq during the detach. The irq for the second asic (devpriv->irq2) will also be requested during the attach if required. The freeing of this irq will be handled by the driver during the detach. Move the board reset and interrupt request code so it occurs early in the attach. We can then check dev->irq and devpriv->irq2 to see if the subdevice command support actually needs to be initialized. This also simplifies the interrupt handler. The irq can be simply checked against dev->irq and devpriv->irq2 to see which asic caused the interrupt. 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 Cc: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/pcmuio.c | 81 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c index 954fa96..dfa7280 100644 --- a/drivers/staging/comedi/drivers/pcmuio.c +++ b/drivers/staging/comedi/drivers/pcmuio.c @@ -146,10 +146,10 @@ struct pcmuio_subdev_private { struct pcmuio_private { struct { - unsigned int irq; spinlock_t spinlock; } asics[PCMUIO_MAX_ASICS]; struct pcmuio_subdev_private *sprivs; + unsigned int irq2; }; static void pcmuio_write(struct comedi_device *dev, unsigned int val, @@ -390,19 +390,14 @@ 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) + handled += pcmuio_handle_asic_interrupt(dev, 0); + if (irq == devpriv->irq2) + handled += pcmuio_handle_asic_interrupt(dev, 1); + + return handled ? IRQ_HANDLED : IRQ_NONE; } static int pcmuio_start_intr(struct comedi_device *dev, @@ -591,12 +586,8 @@ 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]; 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) @@ -609,6 +600,29 @@ 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); + + if (it->options[1]) { + /* request the irq for the 1st asic */ + ret = request_irq(it->options[1], pcmuio_interrupt, 0, + dev->board_name, dev); + if (ret == 0) + dev->irq = it->options[1]; + } + + if (board->num_asics == 2) { + if (it->options[2] == dev->irq) { + /* the same irq (or none) is used by both asics */ + devpriv->irq2 = it->options[2]; + } else if (it->options[2]) { + /* request the irq for the 2nd asic */ + ret = request_irq(it->options[2], pcmuio_interrupt, 0, + dev->board_name, dev); + if (ret == 0) + devpriv->irq2 = it->options[2]; + } + } + n_subdevs = board->num_asics * 2; devpriv->sprivs = kcalloc(n_subdevs, sizeof(*subpriv), GFP_KERNEL); if (!devpriv->sprivs) @@ -630,8 +644,9 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) s->insn_config = pcmuio_dio_insn_config; s->n_chan = 24; - /* subdevices 0 and 2 suppport interrupts */ - if ((sdev_no % 2) == 0) { + /* subdevices 0 and 2 can suppport interrupts */ + if ((sdev_no == 0 && dev->irq) || + (sdev_no == 2 && devpriv->irq2)) { /* setup the interrupt subdevice */ subpriv->intr.asic = sdev_no / 2; dev->read_subdev = s; @@ -647,36 +662,20 @@ 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; if (devpriv) { - for (i = 0; i < PCMUIO_MAX_ASICS; ++i) { - if (devpriv->asics[i].irq) - free_irq(devpriv->asics[i].irq, dev); - } + pcmuio_reset(dev); + + /* free the 2nd irq if used, the core will free the 1st one */ + if (devpriv->irq2 && devpriv->irq2 != dev->irq) + free_irq(devpriv->irq2, dev); + kfree(devpriv->sprivs); } comedi_legacy_detach(dev); -- 2.7.4