Commit 4cde0a6d authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: addi_apci_1032: hook-up the interrupt subdevice

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: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 14696bbe
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
#include "comedi_fc.h" #include "comedi_fc.h"
#include "amcc_s5933.h" #include "amcc_s5933.h"
#include "addi-data/addi_common.h"
/* /*
* I/O Register Map * I/O Register Map
*/ */
...@@ -47,72 +45,191 @@ ...@@ -47,72 +45,191 @@
#define APCI1032_CTRL_INT_AND (1 << 1) #define APCI1032_CTRL_INT_AND (1 << 1)
#define APCI1032_CTRL_INT_ENA (1 << 2) #define APCI1032_CTRL_INT_ENA (1 << 2)
/* Digital Input IRQ Function Selection */ struct apci1032_private {
#define ADDIDATA_OR 0 unsigned int mode1; /* rising-edge/high level channels */
#define ADDIDATA_AND 1 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 * Change-Of-State (COS) interrupt configuration
* 0 Disable Digital Input Interrupt *
* data[1] : 0 ADDIDATA Interrupt OR LOGIC * Channels 0 to 15 are interruptible. These channels can be configured
* : 1 ADDIDATA Interrupt AND LOGIC * to generate interrupts based on AND/OR logic for the desired channels.
* data[2] : Interrupt mask for the mode 1 *
* data[3] : Interrupt mask for the mode 2 * 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, static int apci1032_cos_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_insn *insn, struct comedi_insn *insn,
unsigned int *data) unsigned int *data)
{ {
unsigned int ui_TmpValue; struct apci1032_private *devpriv = dev->private;
unsigned int ul_Command1 = 0;
unsigned int ul_Command2 = 0; switch (data[0]) {
case INSN_CONFIG_DIGITAL_TRIG:
/*******************************/ devpriv->mode1 = data[2];
/* Set the digital input logic */ devpriv->mode2 = data[3];
/*******************************/
if (data[0] == ADDIDATA_ENABLE) { if (devpriv->mode1 || devpriv->mode2) {
ul_Command1 = ul_Command1 | data[2]; devpriv->ctrl = APCI1032_CTRL_INT_ENA;
ul_Command2 = ul_Command2 | data[3]; if (data[1] == 1)
outl(ul_Command1, dev->iobase + APCI1032_MODE1_REG); devpriv->ctrl = APCI1032_CTRL_INT_AND;
outl(ul_Command2, dev->iobase + APCI1032_MODE2_REG); else
if (data[1] == ADDIDATA_OR) { devpriv->ctrl = APCI1032_CTRL_INT_OR;
outl(APCI1032_CTRL_INT_ENA | } else {
APCI1032_CTRL_INT_OR, devpriv->ctrl = 0;
dev->iobase + APCI1032_CTRL_REG); apci1032_reset(dev);
ui_TmpValue = }
inl(dev->iobase + APCI1032_CTRL_REG); break;
} /* if (data[1] == ADDIDATA_OR) */ default:
else return -EINVAL;
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) */
return insn->n; 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) static irqreturn_t apci1032_interrupt(int irq, void *d)
{ {
struct comedi_device *dev = d; struct comedi_device *dev = d;
struct comedi_subdevice *s = dev->read_subdev;
unsigned int ctrl; unsigned int ctrl;
/* disable the interrupt */ /* disable the interrupt */
ctrl = inl(dev->iobase + APCI1032_CTRL_REG); ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG); outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
ui_InterruptStatus = inl(dev->iobase + APCI1032_STATUS_REG); s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
ui_InterruptStatus = ui_InterruptStatus & 0X0000FFFF; comedi_buf_put(s->async, s->state);
s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
comedi_event(dev, s);
/* enable the interrupt */ /* enable the interrupt */
outl(ctrl, dev->iobase + APCI1032_CTRL_REG); outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
...@@ -130,27 +247,20 @@ static int apci1032_di_insn_bits(struct comedi_device *dev, ...@@ -130,27 +247,20 @@ static int apci1032_di_insn_bits(struct comedi_device *dev,
return insn->n; 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, static int apci1032_attach_pci(struct comedi_device *dev,
struct pci_dev *pcidev) struct pci_dev *pcidev)
{ {
struct apci1032_private *devpriv;
struct comedi_subdevice *s; struct comedi_subdevice *s;
int ret; int ret;
dev->board_name = dev->driver->driver_name; 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); ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret) if (ret)
return ret; return ret;
...@@ -178,14 +288,22 @@ static int apci1032_attach_pci(struct comedi_device *dev, ...@@ -178,14 +288,22 @@ static int apci1032_attach_pci(struct comedi_device *dev,
s->range_table = &range_digital; s->range_table = &range_digital;
s->insn_bits = apci1032_di_insn_bits; s->insn_bits = apci1032_di_insn_bits;
/* Change-Of-State (COS) interrupt subdevice */
s = &dev->subdevices[1];
if (dev->irq) { if (dev->irq) {
s = &dev->subdevices[1]; dev->read_subdev = s;
s->type = COMEDI_SUBD_DI; s->type = COMEDI_SUBD_DI | SDF_CMD_READ;
s->subdev_flags = SDF_READABLE; s->subdev_flags = SDF_READABLE;
s->n_chan = 1; s->n_chan = 1;
s->maxdata = 1; s->maxdata = 1;
s->range_table = &range_digital; 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); apci1032_reset(dev);
......
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