diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/das16.c')
-rw-r--r-- | drivers/staging/comedi/drivers/das16.c | 469 |
1 files changed, 207 insertions, 262 deletions
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c index 2436057304a3..2c20311120f1 100644 --- a/drivers/staging/comedi/drivers/das16.c +++ b/drivers/staging/comedi/drivers/das16.c @@ -22,28 +22,17 @@ * Driver: das16 * Description: DAS16 compatible boards * Author: Sam Moore, Warren Jasper, ds, Chris Baugher, Frank Hess, Roman Fietze - * Devices: (Keithley Metrabyte) DAS-16 [das-16] - * (Keithley Metrabyte) DAS-16G [das-16g] - * (Keithley Metrabyte) DAS-16F [das-16f] - * (Keithley Metrabyte) DAS-1201 [das-1201] - * (Keithley Metrabyte) DAS-1202 [das-1202] - * (Keithley Metrabyte) DAS-1401 [das-1401] - * (Keithley Metrabyte) DAS-1402 [das-1402] - * (Keithley Metrabyte) DAS-1601 [das-1601] - * (Keithley Metrabyte) DAS-1602 [das-1602] - * (ComputerBoards) PC104-DAS16/JR [pc104-das16jr] - * (ComputerBoards) PC104-DAS16JR/16 [pc104-das16jr/16] - * (ComputerBoards) CIO-DAS16 [cio-das16] - * (ComputerBoards) CIO-DAS16F [cio-das16/f] - * (ComputerBoards) CIO-DAS16/JR [cio-das16/jr] - * (ComputerBoards) CIO-DAS16JR/16 [cio-das16jr/16] - * (ComputerBoards) CIO-DAS1401/12 [cio-das1401/12] - * (ComputerBoards) CIO-DAS1402/12 [cio-das1402/12] - * (ComputerBoards) CIO-DAS1402/16 [cio-das1402/16] - * (ComputerBoards) CIO-DAS1601/12 [cio-das1601/12] - * (ComputerBoards) CIO-DAS1602/12 [cio-das1602/12] - * (ComputerBoards) CIO-DAS1602/16 [cio-das1602/16] - * (ComputerBoards) CIO-DAS16/330 [cio-das16/330] + * Devices: [Keithley Metrabyte] DAS-16 (das-16), DAS-16G (das-16g), + * DAS-16F (das-16f), DAS-1201 (das-1201), DAS-1202 (das-1202), + * DAS-1401 (das-1401), DAS-1402 (das-1402), DAS-1601 (das-1601), + * DAS-1602 (das-1602), + * [ComputerBoards] PC104-DAS16/JR (pc104-das16jr), + * PC104-DAS16JR/16 (pc104-das16jr/16), CIO-DAS16 (cio-das16), + * CIO-DAS16F (cio-das16/f), CIO-DAS16/JR (cio-das16/jr), + * CIO-DAS16JR/16 (cio-das16jr/16), CIO-DAS1401/12 (cio-das1401/12), + * CIO-DAS1402/12 (cio-das1402/12), CIO-DAS1402/16 (cio-das1402/16), + * CIO-DAS1601/12 (cio-das1601/12), CIO-DAS1602/12 (cio-das1602/12), + * CIO-DAS1602/16 (cio-das1602/16), CIO-DAS16/330 (cio-das16/330) * Status: works * Updated: 2003-10-12 * @@ -82,17 +71,14 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/delay.h> -#include <linux/pci.h> #include <linux/interrupt.h> -#include <asm/dma.h> - #include "../comedidev.h" +#include "comedi_isadma.h" +#include "comedi_fc.h" #include "8253.h" #include "8255.h" -#include "comedi_fc.h" #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ @@ -451,86 +437,37 @@ static inline int timer_period(void) } struct das16_private_struct { + struct comedi_isadma *dma; unsigned int clockbase; unsigned int ctrl_reg; - unsigned long adc_byte_count; unsigned int divisor1; unsigned int divisor2; - unsigned int dma_chan; - uint16_t *dma_buffer[2]; - dma_addr_t dma_buffer_addr[2]; - unsigned int current_buffer; - unsigned int dma_transfer_size; - struct comedi_lrange *user_ai_range_table; - struct comedi_lrange *user_ao_range_table; struct timer_list timer; - short timer_running; unsigned long extra_iobase; unsigned int can_burst:1; + unsigned int timer_running:1; }; -static void das16_ai_enable(struct comedi_device *dev, - unsigned int mode, unsigned int src) -{ - struct das16_private_struct *devpriv = dev->private; - - devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | - DAS16_CTRL_DMAE | - DAS16_CTRL_PACING_MASK); - devpriv->ctrl_reg |= mode; - - if (src == TRIG_EXT) - devpriv->ctrl_reg |= DAS16_CTRL_EXT_PACER; - else - devpriv->ctrl_reg |= DAS16_CTRL_INT_PACER; - outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); -} - -static void das16_ai_disable(struct comedi_device *dev) +static void das16_ai_setup_dma(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int unread_samples) { struct das16_private_struct *devpriv = dev->private; + struct comedi_isadma *dma = devpriv->dma; + struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; + unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize); + unsigned int nsamples; - /* disable interrupts, dma and pacer clocked conversions */ - devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | - DAS16_CTRL_DMAE | - DAS16_CTRL_PACING_MASK); - outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); -} - -/* the pc104-das16jr (at least) has problems if the dma - transfer is interrupted in the middle of transferring - a 16 bit sample, so this function takes care to get - an even transfer count after disabling dma - channel. -*/ -static int disable_dma_on_even(struct comedi_device *dev) -{ - struct das16_private_struct *devpriv = dev->private; - static const int disable_limit = 100; - static const int enable_timeout = 100; - int residue; - int new_residue; - int i; - int j; - - disable_dma(devpriv->dma_chan); - residue = get_dma_residue(devpriv->dma_chan); - for (i = 0; i < disable_limit && (residue % 2); ++i) { - enable_dma(devpriv->dma_chan); - for (j = 0; j < enable_timeout; ++j) { - udelay(2); - new_residue = get_dma_residue(devpriv->dma_chan); - if (new_residue != residue) - break; - } - disable_dma(devpriv->dma_chan); - residue = get_dma_residue(devpriv->dma_chan); - } - if (i == disable_limit) { - dev_err(dev->class_dev, - "failed to get an even dma transfer, could be trouble\n"); + /* + * Determine dma size based on the buffer size plus the number of + * unread samples and the number of samples remaining in the command. + */ + nsamples = comedi_nsamples_left(s, max_samples + unread_samples); + if (nsamples > unread_samples) { + nsamples -= unread_samples; + desc->size = comedi_samples_to_bytes(s, nsamples); + comedi_isadma_program(desc); } - return residue; } static void das16_interrupt(struct comedi_device *dev) @@ -539,11 +476,12 @@ static void das16_interrupt(struct comedi_device *dev) struct comedi_subdevice *s = dev->read_subdev; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; + struct comedi_isadma *dma = devpriv->dma; + struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; unsigned long spin_flags; - unsigned long dma_flags; + unsigned int residue; + unsigned int nbytes; unsigned int nsamples; - int num_bytes, residue; - int buffer_index; spin_lock_irqsave(&dev->spinlock, spin_flags); if (!(devpriv->ctrl_reg & DAS16_CTRL_DMAE)) { @@ -551,42 +489,36 @@ static void das16_interrupt(struct comedi_device *dev) return; } - dma_flags = claim_dma_lock(); - clear_dma_ff(devpriv->dma_chan); - residue = disable_dma_on_even(dev); + /* + * The pc104-das16jr (at least) has problems if the dma + * transfer is interrupted in the middle of transferring + * a 16 bit sample. + */ + residue = comedi_isadma_disable_on_sample(desc->chan, + comedi_bytes_per_sample(s)); - /* figure out how many points to read */ - if (residue > devpriv->dma_transfer_size) { + /* figure out how many samples to read */ + if (residue > desc->size) { dev_err(dev->class_dev, "residue > transfer size!\n"); - async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; - num_bytes = 0; - } else - num_bytes = devpriv->dma_transfer_size - residue; - - if (cmd->stop_src == TRIG_COUNT && - num_bytes >= devpriv->adc_byte_count) { - num_bytes = devpriv->adc_byte_count; - async->events |= COMEDI_CB_EOA; + async->events |= COMEDI_CB_ERROR; + nbytes = 0; + } else { + nbytes = desc->size - residue; } + nsamples = comedi_bytes_to_samples(s, nbytes); - buffer_index = devpriv->current_buffer; - devpriv->current_buffer = (devpriv->current_buffer + 1) % 2; - devpriv->adc_byte_count -= num_bytes; - - /* re-enable dma */ - if ((async->events & COMEDI_CB_EOA) == 0) { - set_dma_addr(devpriv->dma_chan, - devpriv->dma_buffer_addr[devpriv->current_buffer]); - set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); - enable_dma(devpriv->dma_chan); + /* restart DMA if more samples are needed */ + if (nsamples) { + dma->cur_dma = 1 - dma->cur_dma; + das16_ai_setup_dma(dev, s, nsamples); } - release_dma_lock(dma_flags); spin_unlock_irqrestore(&dev->spinlock, spin_flags); - nsamples = comedi_bytes_to_samples(s, num_bytes); - comedi_buf_write_samples(s, devpriv->dma_buffer[buffer_index], - nsamples); + comedi_buf_write_samples(s, desc->virt_addr, nsamples); + + if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) + async->events |= COMEDI_CB_EOA; comedi_handle_events(dev, s); } @@ -605,6 +537,29 @@ static void das16_timer_interrupt(unsigned long arg) spin_unlock_irqrestore(&dev->spinlock, flags); } +static void das16_ai_set_mux_range(struct comedi_device *dev, + unsigned int first_chan, + unsigned int last_chan, + unsigned int range) +{ + const struct das16_board *board = dev->board_ptr; + + /* set multiplexer */ + outb(first_chan | (last_chan << 4), dev->iobase + DAS16_MUX_REG); + + /* some boards do not have programmable gain */ + if (board->ai_pg == das16_pg_none) + return; + + /* + * Set gain (this is also burst rate register but according to + * computer boards manual, burst rate does nothing, even on + * keithley cards). + */ + outb((das16_gainlists[board->ai_pg])[range], + dev->iobase + DAS16_GAIN_REG); +} + static int das16_ai_check_chanlist(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) @@ -755,13 +710,15 @@ static unsigned int das16_set_pacer(struct comedi_device *dev, unsigned int ns, static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) { - const struct das16_board *board = dev->board_ptr; struct das16_private_struct *devpriv = dev->private; + struct comedi_isadma *dma = devpriv->dma; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; + unsigned int first_chan = CR_CHAN(cmd->chanlist[0]); + unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]); + unsigned int range = CR_RANGE(cmd->chanlist[0]); unsigned int byte; unsigned long flags; - int range; if (cmd->flags & CMDF_PRIORITY) { dev_err(dev->class_dev, @@ -769,24 +726,11 @@ static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) return -1; } - devpriv->adc_byte_count = cmd->stop_arg * comedi_bytes_per_scan(s); - if (devpriv->can_burst) outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV_REG); - /* set scan limits */ - byte = CR_CHAN(cmd->chanlist[0]); - byte |= CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]) << 4; - outb(byte, dev->iobase + DAS16_MUX_REG); - - /* set gain (this is also burst rate register but according to - * computer boards manual, burst rate does nothing, even on - * keithley cards) */ - if (board->ai_pg != das16_pg_none) { - range = CR_RANGE(cmd->chanlist[0]); - outb((das16_gainlists[board->ai_pg])[range], - dev->iobase + DAS16_GAIN_REG); - } + /* set mux and range for chanlist scan */ + das16_ai_set_mux_range(dev, first_chan, last_chan, range); /* set counter mode and counts */ cmd->convert_arg = das16_set_pacer(dev, cmd->convert_arg, cmd->flags); @@ -805,19 +749,9 @@ static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) } outb(byte, dev->iobase + DAS16_PACER_REG); - /* set up dma transfer */ - flags = claim_dma_lock(); - disable_dma(devpriv->dma_chan); - /* clear flip-flop to make sure 2-byte registers for - * count and address get set correctly */ - clear_dma_ff(devpriv->dma_chan); - devpriv->current_buffer = 0; - set_dma_addr(devpriv->dma_chan, - devpriv->dma_buffer_addr[devpriv->current_buffer]); - devpriv->dma_transfer_size = DAS16_DMA_SIZE; - set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); - enable_dma(devpriv->dma_chan); - release_dma_lock(flags); + /* set up dma transfer */ + dma->cur_dma = 0; + das16_ai_setup_dma(dev, s, 0); /* set up timer */ spin_lock_irqsave(&dev->spinlock, flags); @@ -825,7 +759,14 @@ static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) devpriv->timer.expires = jiffies + timer_period(); add_timer(&devpriv->timer); - das16_ai_enable(dev, DAS16_CTRL_DMAE, cmd->convert_src); + /* enable DMA interrupt with external or internal pacing */ + devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | DAS16_CTRL_PACING_MASK); + devpriv->ctrl_reg |= DAS16_CTRL_DMAE; + if (cmd->convert_src == TRIG_EXT) + devpriv->ctrl_reg |= DAS16_CTRL_EXT_PACER; + else + devpriv->ctrl_reg |= DAS16_CTRL_INT_PACER; + outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); if (devpriv->can_burst) outb(0, dev->iobase + DAS1600_CONV_REG); @@ -837,12 +778,17 @@ static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) static int das16_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { struct das16_private_struct *devpriv = dev->private; + struct comedi_isadma *dma = devpriv->dma; unsigned long flags; spin_lock_irqsave(&dev->spinlock, flags); - das16_ai_disable(dev); - disable_dma(devpriv->dma_chan); + /* disable interrupts, dma and pacer clocked conversions */ + devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | DAS16_CTRL_DMAE | + DAS16_CTRL_PACING_MASK); + outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); + + comedi_isadma_disable(dma->chan); /* disable SW timer */ if (devpriv->timer_running) { @@ -893,23 +839,14 @@ static int das16_ai_insn_read(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data) { - const struct das16_board *board = dev->board_ptr; unsigned int chan = CR_CHAN(insn->chanspec); unsigned int range = CR_RANGE(insn->chanspec); unsigned int val; int ret; int i; - das16_ai_disable(dev); - - /* set multiplexer */ - outb(chan | (chan << 4), dev->iobase + DAS16_MUX_REG); - - /* set gain */ - if (board->ai_pg != das16_pg_none) { - outb((das16_gainlists[board->ai_pg])[range], - dev->iobase + DAS16_GAIN_REG); - } + /* set mux and range for single channel */ + das16_ai_set_mux_range(dev, chan, chan, range); for (i = 0; i < insn->n; i++) { /* trigger conversion */ @@ -1001,14 +938,107 @@ static void das16_reset(struct comedi_device *dev) outb(0, dev->iobase + DAS16_TIMER_BASE_REG + i8254_control_reg); } +static void das16_alloc_dma(struct comedi_device *dev, unsigned int dma_chan) +{ + struct das16_private_struct *devpriv = dev->private; + + /* only DMA channels 3 and 1 are valid */ + if (!(dma_chan == 1 || dma_chan == 3)) + return; + + /* DMA uses two buffers */ + devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan, + DAS16_DMA_SIZE, COMEDI_ISADMA_READ); + if (devpriv->dma) { + init_timer(&devpriv->timer); + devpriv->timer.function = das16_timer_interrupt; + devpriv->timer.data = (unsigned long)dev; + } +} + +static void das16_free_dma(struct comedi_device *dev) +{ + struct das16_private_struct *devpriv = dev->private; + + if (devpriv) { + if (devpriv->timer.data) + del_timer_sync(&devpriv->timer); + comedi_isadma_free(devpriv->dma); + } +} + +static const struct comedi_lrange *das16_ai_range(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_devconfig *it, + unsigned int pg_type, + unsigned int status) +{ + unsigned int min = it->options[4]; + unsigned int max = it->options[5]; + + /* get any user-defined input range */ + if (pg_type == das16_pg_none && (min || max)) { + struct comedi_lrange *lrange; + struct comedi_krange *krange; + + /* allocate single-range range table */ + lrange = comedi_alloc_spriv(s, + sizeof(*lrange) + sizeof(*krange)); + if (!lrange) + return &range_unknown; + + /* initialize ai range */ + lrange->length = 1; + krange = lrange->range; + krange->min = min; + krange->max = max; + krange->flags = UNIT_volt; + + return lrange; + } + + /* use software programmable range */ + if (status & DAS16_STATUS_UNIPOLAR) + return das16_ai_uni_lranges[pg_type]; + return das16_ai_bip_lranges[pg_type]; +} + +static const struct comedi_lrange *das16_ao_range(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_devconfig *it) +{ + unsigned int min = it->options[6]; + unsigned int max = it->options[7]; + + /* get any user-defined output range */ + if (min || max) { + struct comedi_lrange *lrange; + struct comedi_krange *krange; + + /* allocate single-range range table */ + lrange = comedi_alloc_spriv(s, + sizeof(*lrange) + sizeof(*krange)); + if (!lrange) + return &range_unknown; + + /* initialize ao range */ + lrange->length = 1; + krange = lrange->range; + krange->min = min; + krange->max = max; + krange->flags = UNIT_volt; + + return lrange; + } + + return &range_unknown; +} + static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) { const struct das16_board *board = dev->board_ptr; struct das16_private_struct *devpriv; struct comedi_subdevice *s; - struct comedi_lrange *lrange; - struct comedi_krange *krange; - unsigned int dma_chan = it->options[2]; unsigned int status; int ret; @@ -1063,72 +1093,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) devpriv->clockbase = I8254_OSC_BASE_1MHZ; } - /* initialize dma */ - if (dma_chan == 1 || dma_chan == 3) { - unsigned long flags; - int i; - - if (request_dma(dma_chan, dev->board_name)) { - dev_err(dev->class_dev, - "failed to request dma channel %i\n", - dma_chan); - return -EINVAL; - } - devpriv->dma_chan = dma_chan; - - /* allocate dma buffers */ - for (i = 0; i < 2; i++) { - void *p; - - p = pci_alloc_consistent(NULL, DAS16_DMA_SIZE, - &devpriv->dma_buffer_addr[i]); - if (!p) - return -ENOMEM; - devpriv->dma_buffer[i] = p; - } - - flags = claim_dma_lock(); - disable_dma(devpriv->dma_chan); - set_dma_mode(devpriv->dma_chan, DMA_MODE_READ); - release_dma_lock(flags); - - init_timer(&devpriv->timer); - devpriv->timer.function = das16_timer_interrupt; - devpriv->timer.data = (unsigned long)dev; - } - - /* get any user-defined input range */ - if (board->ai_pg == das16_pg_none && - (it->options[4] || it->options[5])) { - /* allocate single-range range table */ - lrange = kzalloc(sizeof(*lrange) + sizeof(*krange), GFP_KERNEL); - if (!lrange) - return -ENOMEM; - - /* initialize ai range */ - devpriv->user_ai_range_table = lrange; - lrange->length = 1; - krange = devpriv->user_ai_range_table->range; - krange->min = it->options[4]; - krange->max = it->options[5]; - krange->flags = UNIT_volt; - } - - /* get any user-defined output range */ - if (it->options[6] || it->options[7]) { - /* allocate single-range range table */ - lrange = kzalloc(sizeof(*lrange) + sizeof(*krange), GFP_KERNEL); - if (!lrange) - return -ENOMEM; - - /* initialize ao range */ - devpriv->user_ao_range_table = lrange; - lrange->length = 1; - krange = devpriv->user_ao_range_table->range; - krange->min = it->options[6]; - krange->max = it->options[7]; - krange->flags = UNIT_volt; - } + das16_alloc_dma(dev, it->options[2]); ret = comedi_alloc_subdevices(dev, 4 + board->has_8255); if (ret) @@ -1149,15 +1114,9 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) } s->len_chanlist = s->n_chan; s->maxdata = board->ai_maxdata; - if (devpriv->user_ai_range_table) { /* user defined ai range */ - s->range_table = devpriv->user_ai_range_table; - } else if (status & DAS16_STATUS_UNIPOLAR) { - s->range_table = das16_ai_uni_lranges[board->ai_pg]; - } else { - s->range_table = das16_ai_bip_lranges[board->ai_pg]; - } + s->range_table = das16_ai_range(dev, s, it, board->ai_pg, status); s->insn_read = das16_ai_insn_read; - if (devpriv->dma_chan) { + if (devpriv->dma) { dev->read_subdev = s; s->subdev_flags |= SDF_CMD_READ; s->do_cmdtest = das16_cmd_test; @@ -1173,7 +1132,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) s->subdev_flags = SDF_WRITABLE; s->n_chan = 2; s->maxdata = 0x0fff; - s->range_table = devpriv->user_ao_range_table; + s->range_table = das16_ao_range(dev, s, it); s->insn_write = das16_ao_insn_write; ret = comedi_alloc_subdev_readback(s); @@ -1230,25 +1189,11 @@ static void das16_detach(struct comedi_device *dev) { const struct das16_board *board = dev->board_ptr; struct das16_private_struct *devpriv = dev->private; - int i; if (devpriv) { - if (devpriv->timer.data) - del_timer_sync(&devpriv->timer); if (dev->iobase) das16_reset(dev); - - for (i = 0; i < 2; i++) { - if (devpriv->dma_buffer[i]) - pci_free_consistent(NULL, DAS16_DMA_SIZE, - devpriv->dma_buffer[i], - devpriv-> - dma_buffer_addr[i]); - } - if (devpriv->dma_chan) - free_dma(devpriv->dma_chan); - kfree(devpriv->user_ai_range_table); - kfree(devpriv->user_ao_range_table); + das16_free_dma(dev); if (devpriv->extra_iobase) release_region(devpriv->extra_iobase, |