staging: comedi: ni_6527: support INSN_CONFIG_DIGITAL_TRIG

The "edge detection interrupt" subdevice supports the
`INSN_CONFIG_CHANGE_NOTIFY` comedi instruction which is only supported
by one other driver.  The `INSN_CONFIG_DIGITAL_TRIG` comedi instruction
is more flexible as it supports both edge and level detection, but is
not currently supported by this driver.

Add partial support for `INSN_CONFIG_DIGITAL_TRIG`, but only for edge
detection.  Make use of the `ni6527_set_edge_detection()` used for
`INSN_CONFIG_CHANGE_NOTIFY`, but add a parameter holding a mask of the
rising and falling edges to be updated and preserve the unmasked edges
when updating.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Ian Abbott 2014-07-21 17:29:05 +01:00 committed by Greg Kroah-Hartman
parent d484e33728
commit 110f9e687c

View File

@ -299,21 +299,38 @@ static int ni6527_intr_insn_bits(struct comedi_device *dev,
} }
static void ni6527_set_edge_detection(struct comedi_device *dev, static void ni6527_set_edge_detection(struct comedi_device *dev,
unsigned int mask,
unsigned int rising, unsigned int rising,
unsigned int falling) unsigned int falling)
{ {
struct ni6527_private *devpriv = dev->private; struct ni6527_private *devpriv = dev->private;
void __iomem *mmio = devpriv->mmio_base; void __iomem *mmio = devpriv->mmio_base;
unsigned int i;
/* enable rising-edge detection channels */ rising &= mask;
writeb(rising & 0xff, mmio + NI6527_RISING_EDGE_REG(0)); falling &= mask;
writeb((rising >> 8) & 0xff, mmio + NI6527_RISING_EDGE_REG(1)); for (i = 0; i < 2; i++) {
writeb((rising >> 16) & 0xff, mmio + NI6527_RISING_EDGE_REG(2)); if (mask & 0xff) {
if (~mask & 0xff) {
/* enable falling-edge detection channels */ /* preserve rising-edge detection channels */
writeb(falling & 0xff, mmio + NI6527_FALLING_EDGE_REG(0)); rising |= readb(mmio +
writeb((falling >> 8) & 0xff, mmio + NI6527_FALLING_EDGE_REG(1)); NI6527_RISING_EDGE_REG(i)) &
writeb((falling >> 16) & 0xff, mmio + NI6527_FALLING_EDGE_REG(2)); (~mask & 0xff);
/* preserve falling-edge detection channels */
falling |= readb(mmio +
NI6527_FALLING_EDGE_REG(i)) &
(~mask & 0xff);
}
/* update rising-edge detection channels */
writeb(rising & 0xff, mmio + NI6527_RISING_EDGE_REG(i));
/* update falling-edge detection channels */
writeb(falling & 0xff,
mmio + NI6527_FALLING_EDGE_REG(i));
}
rising >>= 8;
falling >>= 8;
mask >>= 8;
}
} }
static int ni6527_intr_insn_config(struct comedi_device *dev, static int ni6527_intr_insn_config(struct comedi_device *dev,
@ -321,12 +338,45 @@ static int ni6527_intr_insn_config(struct comedi_device *dev,
struct comedi_insn *insn, struct comedi_insn *insn,
unsigned int *data) unsigned int *data)
{ {
unsigned int mask = 0xffffffff;
unsigned int rising, falling, shift;
switch (data[0]) { switch (data[0]) {
case INSN_CONFIG_CHANGE_NOTIFY: case INSN_CONFIG_CHANGE_NOTIFY:
/* check_insn_config_length() does not check this instruction */ /* check_insn_config_length() does not check this instruction */
if (insn->n != 3) if (insn->n != 3)
return -EINVAL; return -EINVAL;
ni6527_set_edge_detection(dev, data[1], data[2]); rising = data[1];
falling = data[2];
ni6527_set_edge_detection(dev, mask, rising, falling);
break;
case INSN_CONFIG_DIGITAL_TRIG:
/* check trigger number */
if (data[1] != 0)
return -EINVAL;
/* check digital trigger operation */
switch (data[2]) {
case COMEDI_DIGITAL_TRIG_DISABLE:
rising = 0;
falling = 0;
break;
case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
/* check shift amount */
shift = data[3];
if (shift >= s->n_chan) {
mask = 0;
rising = 0;
falling = 0;
} else {
mask <<= shift;
rising = data[4] << shift;
falling = data[5] << shift;
}
break;
default:
return -EINVAL;
}
ni6527_set_edge_detection(dev, mask, rising, falling);
break; break;
default: default:
return -EINVAL; return -EINVAL;