diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/das800.c')
-rw-r--r-- | drivers/staging/comedi/drivers/das800.c | 999 |
1 files changed, 457 insertions, 542 deletions
diff --git a/drivers/staging/comedi/drivers/das800.c b/drivers/staging/comedi/drivers/das800.c index 38f625be812a..9ce6cbcc7ee8 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"); |