From fff105675bfe6645096e2973093a4fe09befd361 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 5 Nov 2012 14:41:52 -0700 Subject: [PATCH] --- yaml --- r: 337681 b: refs/heads/master c: 4cde0a6d51807b5b956f2cc7878a8c4872d8bde5 h: refs/heads/master i: 337679: 3adc549b243d6fa43189562d86efbe6e500aafd5 v: v3 --- [refs] | 2 +- .../staging/comedi/drivers/addi_apci_1032.c | 250 +++++++++++++----- 2 files changed, 185 insertions(+), 67 deletions(-) diff --git a/[refs] b/[refs] index 55a3e5730d37..651a86405223 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 14696bbec450bbc13773a8a8f85577469435fa77 +refs/heads/master: 4cde0a6d51807b5b956f2cc7878a8c4872d8bde5 diff --git a/trunk/drivers/staging/comedi/drivers/addi_apci_1032.c b/trunk/drivers/staging/comedi/drivers/addi_apci_1032.c index 5a300d93ce18..b2736356d850 100644 --- a/trunk/drivers/staging/comedi/drivers/addi_apci_1032.c +++ b/trunk/drivers/staging/comedi/drivers/addi_apci_1032.c @@ -33,8 +33,6 @@ #include "comedi_fc.h" #include "amcc_s5933.h" -#include "addi-data/addi_common.h" - /* * I/O Register Map */ @@ -47,72 +45,191 @@ #define APCI1032_CTRL_INT_AND (1 << 1) #define APCI1032_CTRL_INT_ENA (1 << 2) -/* Digital Input IRQ Function Selection */ -#define ADDIDATA_OR 0 -#define ADDIDATA_AND 1 +struct apci1032_private { + unsigned int mode1; /* rising-edge/high level channels */ + 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 - * 0 Disable Digital Input Interrupt - * data[1] : 0 ADDIDATA Interrupt OR LOGIC - * : 1 ADDIDATA Interrupt AND LOGIC - * data[2] : Interrupt mask for the mode 1 - * data[3] : Interrupt mask for the mode 2 + * Change-Of-State (COS) interrupt configuration + * + * Channels 0 to 15 are interruptible. These channels can be configured + * to generate interrupts based on AND/OR logic for the desired channels. + * + * 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, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) +static int apci1032_cos_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - unsigned int ui_TmpValue; - unsigned int ul_Command1 = 0; - unsigned int ul_Command2 = 0; - - /*******************************/ - /* Set the digital input logic */ - /*******************************/ - if (data[0] == ADDIDATA_ENABLE) { - ul_Command1 = ul_Command1 | data[2]; - ul_Command2 = ul_Command2 | data[3]; - outl(ul_Command1, dev->iobase + APCI1032_MODE1_REG); - outl(ul_Command2, dev->iobase + APCI1032_MODE2_REG); - if (data[1] == ADDIDATA_OR) { - outl(APCI1032_CTRL_INT_ENA | - APCI1032_CTRL_INT_OR, - dev->iobase + APCI1032_CTRL_REG); - ui_TmpValue = - inl(dev->iobase + APCI1032_CTRL_REG); - } /* if (data[1] == ADDIDATA_OR) */ - else - 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) */ + struct apci1032_private *devpriv = dev->private; + + switch (data[0]) { + case INSN_CONFIG_DIGITAL_TRIG: + devpriv->mode1 = data[2]; + devpriv->mode2 = data[3]; + + if (devpriv->mode1 || devpriv->mode2) { + devpriv->ctrl = APCI1032_CTRL_INT_ENA; + if (data[1] == 1) + devpriv->ctrl = APCI1032_CTRL_INT_AND; + else + devpriv->ctrl = APCI1032_CTRL_INT_OR; + } else { + devpriv->ctrl = 0; + apci1032_reset(dev); + } + break; + default: + return -EINVAL; + } 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) { struct comedi_device *dev = d; + struct comedi_subdevice *s = dev->read_subdev; unsigned int ctrl; /* disable the interrupt */ ctrl = inl(dev->iobase + APCI1032_CTRL_REG); outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG); - ui_InterruptStatus = inl(dev->iobase + APCI1032_STATUS_REG); - ui_InterruptStatus = ui_InterruptStatus & 0X0000FFFF; + s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff; + comedi_buf_put(s->async, s->state); + s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; + comedi_event(dev, s); /* enable the interrupt */ outl(ctrl, dev->iobase + APCI1032_CTRL_REG); @@ -130,27 +247,20 @@ static int apci1032_di_insn_bits(struct comedi_device *dev, 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, struct pci_dev *pcidev) { + struct apci1032_private *devpriv; struct comedi_subdevice *s; int ret; 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); if (ret) return ret; @@ -178,14 +288,22 @@ static int apci1032_attach_pci(struct comedi_device *dev, s->range_table = &range_digital; s->insn_bits = apci1032_di_insn_bits; + /* Change-Of-State (COS) interrupt subdevice */ + s = &dev->subdevices[1]; if (dev->irq) { - s = &dev->subdevices[1]; - s->type = COMEDI_SUBD_DI; + dev->read_subdev = s; + s->type = COMEDI_SUBD_DI | SDF_CMD_READ; s->subdev_flags = SDF_READABLE; s->n_chan = 1; s->maxdata = 1; 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);