Commit 931987a0 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: addi_apci_1564: clarify change-of-state interrupt support

This board supports change-of-state interrupts on digital inputs 4 to 19
not 0 to 15.

The current code "works" but it could set inappropriate bits in the mode1
and mode2 registers that setup which channels are enabled. It also doesn't
return the status of the upper 4 channels (19 to 16).

Fix the comment and mask the mode1/mode2 values so that only the interrupt
capable channels can be enabled.

Add the SDF_LSAMPL flag to the subdevice so that 32-bit samples are used
instead of 16-bit ones. This allows returning the upper 4 channels. Use
the remaining bits in the sample to return "event" flags to the user.

The timer and counter subdevices can also generate interrupts and are a bit
hacked. They don't currently follow the comedi API and they use send_sig()
to let the task that know that the interrupt occured. The "event" flags will
be used instead when these subdevices are fixed.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 52caeb4a
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
#define APCI1564_DI_REG 0x00 #define APCI1564_DI_REG 0x00
#define APCI1564_DI_INT_MODE1_REG 0x04 #define APCI1564_DI_INT_MODE1_REG 0x04
#define APCI1564_DI_INT_MODE2_REG 0x08 #define APCI1564_DI_INT_MODE2_REG 0x08
#define APCI1564_DI_INT_MODE_MASK 0x000ffff0 /* chans [19:4] */
#define APCI1564_DI_INT_STATUS_REG 0x0c #define APCI1564_DI_INT_STATUS_REG 0x0c
#define APCI1564_DI_IRQ_REG 0x10 #define APCI1564_DI_IRQ_REG 0x10
#define APCI1564_DI_IRQ_ENA BIT(2) #define APCI1564_DI_IRQ_ENA BIT(2)
...@@ -111,11 +112,18 @@ ...@@ -111,11 +112,18 @@
*/ */
#define APCI1564_COUNTER(x) ((x) * 0x20) #define APCI1564_COUNTER(x) ((x) * 0x20)
/*
* The dev->read_subdev is used to return the interrupt events along with
* the state of the interrupt capable inputs.
*/
#define APCI1564_EVENT_COS BIT(31)
#define APCI1564_EVENT_MASK 0xfff0000f /* all but [19:4] */
struct apci1564_private { struct apci1564_private {
unsigned long eeprom; /* base address of EEPROM register */ unsigned long eeprom; /* base address of EEPROM register */
unsigned long timer; /* base address of 12-bit timer */ unsigned long timer; /* base address of 12-bit timer */
unsigned long counters; /* base address of 32-bit counters */ unsigned long counters; /* base address of 32-bit counters */
unsigned int mode1; /* riding-edge/high level channels */ unsigned int mode1; /* rising-edge/high level channels */
unsigned int mode2; /* falling-edge/low level channels */ unsigned int mode2; /* falling-edge/low level channels */
unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */ unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
struct task_struct *tsk_current; struct task_struct *tsk_current;
...@@ -165,18 +173,18 @@ static irqreturn_t apci1564_interrupt(int irq, void *d) ...@@ -165,18 +173,18 @@ static irqreturn_t apci1564_interrupt(int irq, void *d)
unsigned int ctrl; unsigned int ctrl;
unsigned int chan; unsigned int chan;
s->state &= ~APCI1564_EVENT_MASK;
status = inl(dev->iobase + APCI1564_DI_IRQ_REG); status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
if (status & APCI1564_DI_IRQ_ENA) { if (status & APCI1564_DI_IRQ_ENA) {
/* disable the interrupt */ /* get the COS interrupt state and set the event flag */
s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
s->state &= APCI1564_DI_INT_MODE_MASK;
s->state |= APCI1564_EVENT_COS;
/* clear the interrupt */
outl(status & ~APCI1564_DI_IRQ_ENA, outl(status & ~APCI1564_DI_IRQ_ENA,
dev->iobase + APCI1564_DI_IRQ_REG); dev->iobase + APCI1564_DI_IRQ_REG);
s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG) &
0xffff;
comedi_buf_write_samples(s, &s->state, 1);
comedi_handle_events(dev, s);
/* enable the interrupt */
outl(status, dev->iobase + APCI1564_DI_IRQ_REG); outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
} }
...@@ -214,6 +222,11 @@ static irqreturn_t apci1564_interrupt(int irq, void *d) ...@@ -214,6 +222,11 @@ static irqreturn_t apci1564_interrupt(int irq, void *d)
} }
} }
if (s->state & APCI1564_EVENT_MASK) {
comedi_buf_write_samples(s, &s->state, 1);
comedi_handle_events(dev, s);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -255,7 +268,7 @@ static int apci1564_diag_insn_bits(struct comedi_device *dev, ...@@ -255,7 +268,7 @@ static int apci1564_diag_insn_bits(struct comedi_device *dev,
/* /*
* Change-Of-State (COS) interrupt configuration * Change-Of-State (COS) interrupt configuration
* *
* Channels 0 to 15 are interruptible. These channels can be configured * Channels 4 to 19 are interruptible. These channels can be configured
* to generate interrupts based on AND/OR logic for the desired channels. * to generate interrupts based on AND/OR logic for the desired channels.
* *
* OR logic * OR logic
...@@ -343,6 +356,10 @@ static int apci1564_cos_insn_config(struct comedi_device *dev, ...@@ -343,6 +356,10 @@ static int apci1564_cos_insn_config(struct comedi_device *dev,
default: default:
return -EINVAL; return -EINVAL;
} }
/* ensure the mode bits are in-range for channels [19:4] */
devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK;
devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -409,7 +426,7 @@ static int apci1564_cos_cmd(struct comedi_device *dev, ...@@ -409,7 +426,7 @@ static int apci1564_cos_cmd(struct comedi_device *dev,
{ {
struct apci1564_private *devpriv = dev->private; struct apci1564_private *devpriv = dev->private;
if (!devpriv->ctrl) { if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) {
dev_warn(dev->class_dev, dev_warn(dev->class_dev,
"Interrupts disabled due to mode configuration!\n"); "Interrupts disabled due to mode configuration!\n");
return -EINVAL; return -EINVAL;
...@@ -501,7 +518,7 @@ static int apci1564_auto_attach(struct comedi_device *dev, ...@@ -501,7 +518,7 @@ static int apci1564_auto_attach(struct comedi_device *dev,
if (dev->irq) { if (dev->irq) {
dev->read_subdev = s; dev->read_subdev = s;
s->type = COMEDI_SUBD_DI; s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ; s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL;
s->n_chan = 1; s->n_chan = 1;
s->maxdata = 1; s->maxdata = 1;
s->range_table = &range_digital; s->range_table = &range_digital;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment