aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/das800.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/das800.c')
-rw-r--r--drivers/staging/comedi/drivers/das800.c999
1 files changed, 457 insertions, 542 deletions
diff --git a/drivers/staging/comedi/drivers/das800.c b/drivers/staging/comedi/drivers/das800.c
index 38f625be812..9ce6cbcc7ee 100644
--- a/drivers/staging/comedi/drivers/das800.c
+++ b/drivers/staging/comedi/drivers/das800.c
@@ -95,8 +95,8 @@ cmd triggers supported:
#define IRQ 0x8
#define BUSY 0x80
#define DAS800_GAIN 3
-#define CIO_FFOV 0x8 /* fifo overflow for cio-das802/16 */
-#define CIO_ENHF 0x90 /* interrupt fifo half full for cio-das802/16 */
+#define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
+#define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
#define CONTROL1 0x80
#define CONV_CONTROL 0xa0
#define SCAN_LIMITS 0xc0
@@ -107,6 +107,8 @@ cmd triggers supported:
#define STATUS2_INTE 0X20
#define DAS800_ID 7
+#define DAS802_16_HALF_FIFO_SZ 128
+
struct das800_board {
const char *name;
int ai_speed;
@@ -114,491 +116,204 @@ struct das800_board {
int resolution;
};
-/* analog input ranges */
-static const struct comedi_lrange range_das800_ai = {
- 1,
- {
- RANGE(-5, 5),
- }
-};
-
static const struct comedi_lrange range_das801_ai = {
- 9,
- {
- RANGE(-5, 5),
- RANGE(-10, 10),
- RANGE(0, 10),
- RANGE(-0.5, 0.5),
- RANGE(0, 1),
- RANGE(-0.05, 0.05),
- RANGE(0, 0.1),
- RANGE(-0.01, 0.01),
- RANGE(0, 0.02),
- }
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(0.5),
+ UNI_RANGE(1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(0.02)
+ }
};
static const struct comedi_lrange range_cio_das801_ai = {
- 9,
- {
- RANGE(-5, 5),
- RANGE(-10, 10),
- RANGE(0, 10),
- RANGE(-0.5, 0.5),
- RANGE(0, 1),
- RANGE(-0.05, 0.05),
- RANGE(0, 0.1),
- RANGE(-0.005, 0.005),
- RANGE(0, 0.01),
- }
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(0.5),
+ UNI_RANGE(1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(0.1),
+ BIP_RANGE(0.005),
+ UNI_RANGE(0.01)
+ }
};
static const struct comedi_lrange range_das802_ai = {
- 9,
- {
- RANGE(-5, 5),
- RANGE(-10, 10),
- RANGE(0, 10),
- RANGE(-2.5, 2.5),
- RANGE(0, 5),
- RANGE(-1.25, 1.25),
- RANGE(0, 2.5),
- RANGE(-0.625, 0.625),
- RANGE(0, 1.25),
- }
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(2.5),
+ UNI_RANGE(5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(2.5),
+ BIP_RANGE(0.625),
+ UNI_RANGE(1.25)
+ }
};
static const struct comedi_lrange range_das80216_ai = {
- 8,
- {
- RANGE(-10, 10),
- RANGE(0, 10),
- RANGE(-5, 5),
- RANGE(0, 5),
- RANGE(-2.5, 2.5),
- RANGE(0, 2.5),
- RANGE(-1.25, 1.25),
- RANGE(0, 1.25),
- }
+ 8, {
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ UNI_RANGE(5),
+ BIP_RANGE(2.5),
+ UNI_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(1.25)
+ }
};
-enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
+enum das800_boardinfo {
+ BOARD_DAS800,
+ BOARD_CIODAS800,
+ BOARD_DAS801,
+ BOARD_CIODAS801,
+ BOARD_DAS802,
+ BOARD_CIODAS802,
+ BOARD_CIODAS80216,
+};
static const struct das800_board das800_boards[] = {
- {
- .name = "das-800",
- .ai_speed = 25000,
- .ai_range = &range_das800_ai,
- .resolution = 12,
- },
- {
- .name = "cio-das800",
- .ai_speed = 20000,
- .ai_range = &range_das800_ai,
- .resolution = 12,
- },
- {
- .name = "das-801",
- .ai_speed = 25000,
- .ai_range = &range_das801_ai,
- .resolution = 12,
- },
- {
- .name = "cio-das801",
- .ai_speed = 20000,
- .ai_range = &range_cio_das801_ai,
- .resolution = 12,
- },
- {
- .name = "das-802",
- .ai_speed = 25000,
- .ai_range = &range_das802_ai,
- .resolution = 12,
- },
- {
- .name = "cio-das802",
- .ai_speed = 20000,
- .ai_range = &range_das802_ai,
- .resolution = 12,
- },
- {
- .name = "cio-das802/16",
- .ai_speed = 10000,
- .ai_range = &range_das80216_ai,
- .resolution = 16,
- },
+ [BOARD_DAS800] = {
+ .name = "das-800",
+ .ai_speed = 25000,
+ .ai_range = &range_bipolar5,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS800] = {
+ .name = "cio-das800",
+ .ai_speed = 20000,
+ .ai_range = &range_bipolar5,
+ .resolution = 12,
+ },
+ [BOARD_DAS801] = {
+ .name = "das-801",
+ .ai_speed = 25000,
+ .ai_range = &range_das801_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS801] = {
+ .name = "cio-das801",
+ .ai_speed = 20000,
+ .ai_range = &range_cio_das801_ai,
+ .resolution = 12,
+ },
+ [BOARD_DAS802] = {
+ .name = "das-802",
+ .ai_speed = 25000,
+ .ai_range = &range_das802_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS802] = {
+ .name = "cio-das802",
+ .ai_speed = 20000,
+ .ai_range = &range_das802_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS80216] = {
+ .name = "cio-das802/16",
+ .ai_speed = 10000,
+ .ai_range = &range_das80216_ai,
+ .resolution = 16,
+ },
};
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct das800_board *)dev->board_ptr)
-
struct das800_private {
- volatile unsigned int count; /* number of data points left to be taken */
- volatile int forever; /* flag indicating whether we should take data forever */
- unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
- unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
- volatile int do_bits; /* digital output bits */
-};
-
-static int das800_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
-static void das800_detach(struct comedi_device *dev);
-static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
-
-static struct comedi_driver driver_das800 = {
- .driver_name = "das800",
- .module = THIS_MODULE,
- .attach = das800_attach,
- .detach = das800_detach,
- .num_names = ARRAY_SIZE(das800_boards),
- .board_name = &das800_boards[0].name,
- .offset = sizeof(struct das800_board),
+ unsigned int count; /* number of data points left to be taken */
+ unsigned int divisor1; /* counter 1 value for timed conversions */
+ unsigned int divisor2; /* counter 2 value for timed conversions */
+ unsigned int do_bits; /* digital output bits */
+ bool forever; /* flag that we should take data forever */
};
-static irqreturn_t das800_interrupt(int irq, void *d);
-static void enable_das800(struct comedi_device *dev);
-static void disable_das800(struct comedi_device *dev);
-static int das800_ai_do_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd);
-static int das800_ai_do_cmd(struct comedi_device *dev,
- struct comedi_subdevice *s);
-static int das800_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data);
-static int das800_di_rbits(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data);
-static int das800_do_wbits(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data);
-static int das800_probe(struct comedi_device *dev);
-static int das800_set_frequency(struct comedi_device *dev);
-
-/* checks and probes das-800 series board type */
-static int das800_probe(struct comedi_device *dev)
+static void das800_ind_write(struct comedi_device *dev,
+ unsigned val, unsigned reg)
{
- int id_bits;
- unsigned long irq_flags;
- int board;
-
- /* 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
- spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
- id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-
- board = thisboard - das800_boards;
-
- switch (id_bits) {
- case 0x0:
- if (board == das800) {
- dev_dbg(dev->class_dev, "Board model: DAS-800\n");
- return board;
- }
- if (board == ciodas800) {
- dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
- return board;
- }
- dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
- return das800;
- break;
- case 0x2:
- if (board == das801) {
- dev_dbg(dev->class_dev, "Board model: DAS-801\n");
- return board;
- }
- if (board == ciodas801) {
- dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
- return board;
- }
- dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
- return das801;
- break;
- case 0x3:
- if (board == das802) {
- dev_dbg(dev->class_dev, "Board model: DAS-802\n");
- return board;
- }
- if (board == ciodas802) {
- dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
- return board;
- }
- if (board == ciodas80216) {
- dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
- return board;
- }
- dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
- return das802;
- break;
- default:
- dev_dbg(dev->class_dev,
- "Board model: probe returned 0x%x (unknown)\n",
- id_bits);
- return board;
- break;
- }
- return -1;
+ /*
+ * Select dev->iobase + 2 to be desired register
+ * then write to that register.
+ */
+ outb(reg, dev->iobase + DAS800_GAIN);
+ outb(val, dev->iobase + 2);
}
-module_comedi_driver(driver_das800);
+static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
+{
+ /*
+ * Select dev->iobase + 7 to be desired register
+ * then read from that register.
+ */
+ outb(reg, dev->iobase + DAS800_GAIN);
+ return inb(dev->iobase + 7);
+}
-/* interrupt service routine */
-static irqreturn_t das800_interrupt(int irq, void *d)
+static void das800_enable(struct comedi_device *dev)
{
- short i; /* loop index */
- short dataPoint = 0;
- struct comedi_device *dev = d;
+ const struct das800_board *thisboard = comedi_board(dev);
struct das800_private *devpriv = dev->private;
- struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
- struct comedi_async *async;
- int status;
unsigned long irq_flags;
- static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
- /* flags */
- int fifo_empty = 0;
- int fifo_overflow = 0;
- status = inb(dev->iobase + DAS800_STATUS);
- /* if interrupt was not generated by board or driver not attached, quit */
- if (!(status & IRQ))
- return IRQ_NONE;
- if (!(dev->attached))
- return IRQ_HANDLED;
-
- /* wait until here to initialize async, since we will get null dereference
- * if interrupt occurs before driver is fully attached!
- */
- async = s->async;
-
- /* if hardware conversions are not enabled, then quit */
spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
- status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
- /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
- if (status == 0) {
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- return IRQ_HANDLED;
- }
-
- /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
- for (i = 0; i < max_loops; i++) {
- /* read 16 bits from dev->iobase and dev->iobase + 1 */
- dataPoint = inb(dev->iobase + DAS800_LSB);
- dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
- if (thisboard->resolution == 12) {
- fifo_empty = dataPoint & FIFO_EMPTY;
- fifo_overflow = dataPoint & FIFO_OVF;
- if (fifo_overflow)
- break;
- } else {
- fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
- }
- if (fifo_empty)
- break;
- /* strip off extraneous bits for 12 bit cards */
- if (thisboard->resolution == 12)
- dataPoint = (dataPoint >> 4) & 0xfff;
- /* if there are more data points to collect */
- if (devpriv->count > 0 || devpriv->forever == 1) {
- /* write data point to buffer */
- cfc_write_to_buffer(s, dataPoint);
- if (devpriv->count > 0)
- devpriv->count--;
- }
- }
- async->events |= COMEDI_CB_BLOCK;
- /* check for fifo overflow */
- if (thisboard->resolution == 12) {
- fifo_overflow = dataPoint & FIFO_OVF;
- /* else cio-das802/16 */
- } else {
- fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
- }
- if (fifo_overflow) {
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- comedi_error(dev, "DAS800 FIFO overflow");
- das800_cancel(dev, s);
- async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
- comedi_event(dev, s);
- async->events = 0;
- return IRQ_HANDLED;
- }
- if (devpriv->count > 0 || devpriv->forever == 1) {
- /* Re-enable card's interrupt.
- * We already have spinlock, so indirect addressing is safe */
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
- outb(CONTROL1_INTE | devpriv->do_bits,
- dev->iobase + DAS800_CONTROL1);
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- /* otherwise, stop taking data */
- } else {
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- disable_das800(dev); /* disable hardware triggered conversions */
- async->events |= COMEDI_CB_EOA;
- }
- comedi_event(dev, s);
- async->events = 0;
- return IRQ_HANDLED;
+ /* enable fifo-half full interrupts for cio-das802/16 */
+ if (thisboard->resolution == 16)
+ outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
+ /* enable hardware triggering */
+ das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
+ /* enable card's interrupt */
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
}
-static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static void das800_disable(struct comedi_device *dev)
{
- struct das800_private *devpriv;
- struct comedi_subdevice *s;
- unsigned long iobase = it->options[0];
- unsigned int irq = it->options[1];
unsigned long irq_flags;
- int board;
- int ret;
-
- dev_info(dev->class_dev, "das800: io 0x%lx\n", iobase);
- if (irq)
- dev_dbg(dev->class_dev, "irq %u\n", irq);
- devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
- if (!devpriv)
- return -ENOMEM;
- dev->private = devpriv;
-
- if (iobase == 0) {
- dev_err(dev->class_dev,
- "io base address required for das800\n");
- return -EINVAL;
- }
-
- /* check if io addresses are available */
- if (!request_region(iobase, DAS800_SIZE, "das800")) {
- dev_err(dev->class_dev, "I/O port conflict\n");
- return -EIO;
- }
- dev->iobase = iobase;
-
- board = das800_probe(dev);
- if (board < 0) {
- dev_dbg(dev->class_dev, "unable to determine board type\n");
- return -ENODEV;
- }
- dev->board_ptr = das800_boards + board;
-
- /* grab our IRQ */
- if (irq == 1 || irq > 7) {
- dev_err(dev->class_dev, "irq out of range\n");
- return -EINVAL;
- }
- if (irq) {
- if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
- dev_err(dev->class_dev, "unable to allocate irq %u\n",
- irq);
- return -EINVAL;
- }
- }
- dev->irq = irq;
-
- dev->board_name = thisboard->name;
-
- ret = comedi_alloc_subdevices(dev, 3);
- if (ret)
- return ret;
-
- /* analog input subdevice */
- s = &dev->subdevices[0];
- dev->read_subdev = s;
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
- s->n_chan = 8;
- s->len_chanlist = 8;
- s->maxdata = (1 << thisboard->resolution) - 1;
- s->range_table = thisboard->ai_range;
- s->do_cmd = das800_ai_do_cmd;
- s->do_cmdtest = das800_ai_do_cmdtest;
- s->insn_read = das800_ai_rinsn;
- s->cancel = das800_cancel;
-
- /* di */
- s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 3;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = das800_di_rbits;
-
- /* do */
- s = &dev->subdevices[2];
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
- s->n_chan = 4;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = das800_do_wbits;
-
- disable_das800(dev);
-
- /* initialize digital out channels */
spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
- outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
+ /* disable hardware triggering of conversions */
+ das800_ind_write(dev, 0x0, CONV_CONTROL);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
- return 0;
-};
-
-static void das800_detach(struct comedi_device *dev)
-{
- if (dev->iobase)
- release_region(dev->iobase, DAS800_SIZE);
- if (dev->irq)
- free_irq(dev->irq, dev);
-};
-
-static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+static int das800_set_frequency(struct comedi_device *dev)
{
struct das800_private *devpriv = dev->private;
+ int err = 0;
+
+ if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
+ err++;
+ if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
+ err++;
+ if (err)
+ return -1;
- devpriv->forever = 0;
- devpriv->count = 0;
- disable_das800(dev);
return 0;
}
-/* enable_das800 makes the card start taking hardware triggered conversions */
-static void enable_das800(struct comedi_device *dev)
+static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct das800_private *devpriv = dev->private;
- unsigned long irq_flags;
- spin_lock_irqsave(&dev->spinlock, irq_flags);
- /* enable fifo-half full interrupts for cio-das802/16 */
- if (thisboard->resolution == 16)
- outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
- outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
- outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
- outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-}
-
-/* disable_das800 stops hardware triggered conversions */
-static void disable_das800(struct comedi_device *dev)
-{
- unsigned long irq_flags;
- spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
- outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ devpriv->forever = false;
+ devpriv->count = 0;
+ das800_disable(dev);
+ return 0;
}
static int das800_ai_do_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
+ const struct das800_board *thisboard = comedi_board(dev);
struct das800_private *devpriv = dev->private;
int err = 0;
- int tmp;
- int gain, startChan;
- int i;
/* Step 1 : check if triggers are trivially valid */
@@ -644,11 +359,13 @@ static int das800_ai_do_cmdtest(struct comedi_device *dev,
/* step 4: fix up any arguments */
if (cmd->convert_src == TRIG_TIMER) {
- tmp = cmd->convert_arg;
+ int tmp = cmd->convert_arg;
+
/* calculate counter values that give desired timing */
- i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
- &(devpriv->divisor2),
- &(cmd->convert_arg),
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &devpriv->divisor1,
+ &devpriv->divisor2,
+ &cmd->convert_arg,
cmd->flags & TRIG_ROUND_MASK);
if (tmp != cmd->convert_arg)
err++;
@@ -659,18 +376,21 @@ static int das800_ai_do_cmdtest(struct comedi_device *dev,
/* check channel/gain list against card's limitations */
if (cmd->chanlist) {
- gain = CR_RANGE(cmd->chanlist[0]);
- startChan = CR_CHAN(cmd->chanlist[0]);
+ unsigned int chan = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range = CR_RANGE(cmd->chanlist[0]);
+ unsigned int next;
+ int i;
+
for (i = 1; i < cmd->chanlist_len; i++) {
- if (CR_CHAN(cmd->chanlist[i]) !=
- (startChan + i) % N_CHAN_AI) {
- comedi_error(dev,
- "entries in chanlist must be consecutive channels, counting upwards\n");
+ next = cmd->chanlist[i];
+ if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
+ dev_err(dev->class_dev,
+ "chanlist must be consecutive, counting upwards\n");
err++;
}
- if (CR_RANGE(cmd->chanlist[i]) != gain) {
- comedi_error(dev,
- "entries in chanlist must all have the same gain\n");
+ if (CR_RANGE(next) != range) {
+ dev_err(dev->class_dev,
+ "chanlist must all have the same gain\n");
err++;
}
}
@@ -685,32 +405,24 @@ static int das800_ai_do_cmdtest(struct comedi_device *dev,
static int das800_ai_do_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
+ const struct das800_board *thisboard = comedi_board(dev);
struct das800_private *devpriv = dev->private;
- int startChan, endChan, scan, gain;
+ struct comedi_async *async = s->async;
+ unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
+ unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
+ unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
+ unsigned int scan_chans = (end_chan << 3) | start_chan;
int conv_bits;
unsigned long irq_flags;
- struct comedi_async *async = s->async;
- if (!dev->irq) {
- comedi_error(dev,
- "no irq assigned for das-800, cannot do hardware conversions");
- return -1;
- }
-
- disable_das800(dev);
-
- /* set channel scan limits */
- startChan = CR_CHAN(async->cmd.chanlist[0]);
- endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
- scan = (endChan << 3) | startChan;
+ das800_disable(dev);
spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
- outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
+ /* set scan limits */
+ das800_ind_write(dev, scan_chans, SCAN_LIMITS);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
/* set gain */
- gain = CR_RANGE(async->cmd.chanlist[0]);
if (thisboard->resolution == 12 && gain > 0)
gain += 0x7;
gain &= 0xf;
@@ -719,10 +431,10 @@ static int das800_ai_do_cmd(struct comedi_device *dev,
switch (async->cmd.stop_src) {
case TRIG_COUNT:
devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
- devpriv->forever = 0;
+ devpriv->forever = false;
break;
case TRIG_NONE:
- devpriv->forever = 1;
+ devpriv->forever = true;
devpriv->count = 0;
break;
default:
@@ -740,11 +452,6 @@ static int das800_ai_do_cmd(struct comedi_device *dev,
case TRIG_TIMER:
conv_bits |= CASC | ITE;
/* set conversion frequency */
- i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
- &(devpriv->divisor2),
- &(async->cmd.convert_arg),
- async->cmd.
- flags & TRIG_ROUND_MASK);
if (das800_set_frequency(dev) < 0) {
comedi_error(dev, "Error setting up counters");
return -1;
@@ -757,125 +464,333 @@ static int das800_ai_do_cmd(struct comedi_device *dev,
}
spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
- outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
+ das800_ind_write(dev, conv_bits, CONV_CONTROL);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
async->events = 0;
- enable_das800(dev);
+ das800_enable(dev);
return 0;
}
-static int das800_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+static unsigned int das800_ai_get_sample(struct comedi_device *dev)
{
+ unsigned int lsb = inb(dev->iobase + DAS800_LSB);
+ unsigned int msb = inb(dev->iobase + DAS800_MSB);
+
+ return (msb << 8) | lsb;
+}
+
+static irqreturn_t das800_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
struct das800_private *devpriv = dev->private;
- int i, n;
- int chan;
- int range;
- int lsb, msb;
- int timeout = 1000;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s ? s->async : NULL;
unsigned long irq_flags;
+ unsigned int status;
+ unsigned int val;
+ bool fifo_empty;
+ bool fifo_overflow;
+ int i;
- disable_das800(dev); /* disable hardware conversions (enables software conversions) */
+ status = inb(dev->iobase + DAS800_STATUS);
+ if (!(status & IRQ))
+ return IRQ_NONE;
+ if (!dev->attached)
+ return IRQ_HANDLED;
- /* set multiplexer */
- chan = CR_CHAN(insn->chanspec);
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
+ /*
+ * Don't release spinlock yet since we want to make sure
+ * no one else disables hardware conversions.
+ */
+ /* if hardware conversions are not enabled, then quit */
+ if (status == 0) {
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
+ val = das800_ai_get_sample(dev);
+ if (s->maxdata == 0x0fff) {
+ fifo_empty = !!(val & FIFO_EMPTY);
+ fifo_overflow = !!(val & FIFO_OVF);
+ } else {
+ /* cio-das802/16 has no fifo empty status bit */
+ fifo_empty = false;
+ fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
+ CIO_FFOV);
+ }
+ if (fifo_empty || fifo_overflow)
+ break;
+
+ if (s->maxdata == 0x0fff)
+ val >>= 4; /* 12-bit sample */
+
+ /* if there are more data points to collect */
+ if (devpriv->count > 0 || devpriv->forever) {
+ /* write data point to buffer */
+ cfc_write_to_buffer(s, val & s->maxdata);
+ devpriv->count--;
+ }
+ }
+ async->events |= COMEDI_CB_BLOCK;
+
+ if (fifo_overflow) {
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ das800_cancel(dev, s);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ async->events = 0;
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->count > 0 || devpriv->forever) {
+ /* Re-enable card's interrupt.
+ * We already have spinlock, so indirect addressing is safe */
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
+ CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ } else {
+ /* otherwise, stop taking data */
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ das800_disable(dev);
+ async->events |= COMEDI_CB_EOA;
+ }
+ comedi_event(dev, s);
+ async->events = 0;
+ return IRQ_HANDLED;
+}
+
+static int das800_wait_for_conv(struct comedi_device *dev, int timeout)
+{
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
+ return 0;
+ }
+ return -ETIME;
+}
+
+static int das800_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das800_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned long irq_flags;
+ unsigned int val;
+ int ret;
+ int i;
+
+ das800_disable(dev);
+
+ /* set multiplexer */
spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
- outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
+ das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
/* set gain / range */
- range = CR_RANGE(insn->chanspec);
- if (thisboard->resolution == 12 && range)
+ if (s->maxdata == 0x0fff && range)
range += 0x7;
range &= 0xf;
outb(range, dev->iobase + DAS800_GAIN);
udelay(5);
- for (n = 0; n < insn->n; n++) {
+ for (i = 0; i < insn->n; i++) {
/* trigger conversion */
outb_p(0, dev->iobase + DAS800_MSB);
- for (i = 0; i < timeout; i++) {
- if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
- break;
- }
- if (i == timeout) {
- comedi_error(dev, "timeout");
- return -ETIME;
- }
- lsb = inb(dev->iobase + DAS800_LSB);
- msb = inb(dev->iobase + DAS800_MSB);
- if (thisboard->resolution == 12) {
- data[n] = (lsb >> 4) & 0xff;
- data[n] |= (msb << 4);
- } else {
- data[n] = (msb << 8) | lsb;
- }
+ ret = das800_wait_for_conv(dev, 1000);
+ if (ret)
+ return ret;
+
+ val = das800_ai_get_sample(dev);
+ if (s->maxdata == 0x0fff)
+ val >>= 4; /* 12-bit sample */
+ data[i] = val & s->maxdata;
}
- return n;
+ return insn->n;
}
-static int das800_di_rbits(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+static int das800_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- unsigned int bits;
-
- bits = inb(dev->iobase + DAS800_STATUS) >> 4;
- bits &= 0x7;
- data[1] = bits;
- data[0] = 0;
+ data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
return insn->n;
}
-static int das800_do_wbits(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+static int das800_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct das800_private *devpriv = dev->private;
- int wbits;
+ unsigned int mask = data[0];
+ unsigned int bits = data[1];
unsigned long irq_flags;
- /* only set bits that have been masked */
- data[0] &= 0xf;
- wbits = devpriv->do_bits >> 4;
- wbits &= ~data[0];
- wbits |= data[0] & data[1];
- devpriv->do_bits = wbits << 4;
+ if (mask) {
+ s->state &= ~mask;
+ s->state |= (bits & mask);
+ devpriv->do_bits = s->state << 4;
- spin_lock_irqsave(&dev->spinlock, irq_flags);
- outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
- outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
- spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
+ CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ }
- data[1] = wbits;
+ data[1] = s->state;
return insn->n;
}
-/* loads counters with divisor1, divisor2 from private structure */
-static int das800_set_frequency(struct comedi_device *dev)
+static int das800_probe(struct comedi_device *dev)
{
- struct das800_private *devpriv = dev->private;
- int err = 0;
+ const struct das800_board *thisboard = comedi_board(dev);
+ int board = thisboard ? thisboard - das800_boards : -EINVAL;
+ int id_bits;
+ unsigned long irq_flags;
- if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
- err++;
- if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
- err++;
- if (err)
- return -1;
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ id_bits = das800_ind_read(dev, ID) & 0x3;
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- return 0;
+ switch (id_bits) {
+ case 0x0:
+ if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
+ board = BOARD_DAS800;
+ break;
+ case 0x2:
+ if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
+ board = BOARD_DAS801;
+ break;
+ case 0x3:
+ if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
+ board == BOARD_CIODAS80216)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
+ board = BOARD_DAS802;
+ break;
+ default:
+ dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
+ id_bits);
+ board = -EINVAL;
+ break;
+ }
+ return board;
}
+static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct das800_board *thisboard = comedi_board(dev);
+ struct das800_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int irq = it->options[1];
+ unsigned long irq_flags;
+ int board;
+ int ret;
+
+ devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
+ if (!devpriv)
+ return -ENOMEM;
+ dev->private = devpriv;
+
+ ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
+ if (ret)
+ return ret;
+
+ board = das800_probe(dev);
+ if (board < 0) {
+ dev_dbg(dev->class_dev, "unable to determine board type\n");
+ return -ENODEV;
+ }
+ dev->board_ptr = das800_boards + board;
+ thisboard = comedi_board(dev);
+ dev->board_name = thisboard->name;
+
+ if (irq > 1 && irq <= 7) {
+ ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
+ dev);
+ if (ret == 0)
+ dev->irq = irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = thisboard->ai_range;
+ s->insn_read = das800_ai_insn_read;
+ if (dev->irq) {
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 8;
+ s->do_cmdtest = das800_ai_do_cmdtest;
+ s->do_cmd = das800_ai_do_cmd;
+ s->cancel = das800_cancel;
+ }
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_do_insn_bits;
+
+ das800_disable(dev);
+
+ /* initialize digital out channels */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return 0;
+};
+
+static struct comedi_driver driver_das800 = {
+ .driver_name = "das800",
+ .module = THIS_MODULE,
+ .attach = das800_attach,
+ .detach = comedi_legacy_detach,
+ .num_names = ARRAY_SIZE(das800_boards),
+ .board_name = &das800_boards[0].name,
+ .offset = sizeof(struct das800_board),
+};
+module_comedi_driver(driver_das800);
+
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");