diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/dt2811.c')
-rw-r--r-- | drivers/staging/comedi/drivers/dt2811.c | 645 |
1 files changed, 0 insertions, 645 deletions
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c deleted file mode 100644 index 0eb5e6ba6916..000000000000 --- a/drivers/staging/comedi/drivers/dt2811.c +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Comedi driver for Data Translation DT2811 - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) David A. Schleef <ds@schleef.org> - */ - -/* - * Driver: dt2811 - * Description: Data Translation DT2811 - * Author: ds - * Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh) - * Status: works - * - * Configuration options: - * [0] - I/O port base address - * [1] - IRQ (optional, needed for async command support) - * [2] - A/D reference (# of analog inputs) - * 0 = single-ended (16 channels) - * 1 = differential (8 channels) - * 2 = pseudo-differential (16 channels) - * [3] - A/D range (deprecated, see below) - * [4] - D/A 0 range (deprecated, see below) - * [5] - D/A 1 range (deprecated, see below) - * - * Notes: - * - A/D ranges are not programmable but the gain is. The AI subdevice has - * a range_table containing all the possible analog input range/gain - * options for the dt2811-pgh or dt2811-pgl. Use the range that matches - * your board configuration and the desired gain to correctly convert - * between data values and physical units and to set the correct output - * gain. - * - D/A ranges are not programmable. The AO subdevice has a range_table - * containing all the possible analog output ranges. Use the range - * that matches your board configuration to convert between data - * values and physical units. - */ - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/delay.h> - -#include "../comedidev.h" - -/* - * Register I/O map - */ -#define DT2811_ADCSR_REG 0x00 /* r/w A/D Control/Status */ -#define DT2811_ADCSR_ADDONE BIT(7) /* r 1=A/D conv done */ -#define DT2811_ADCSR_ADERROR BIT(6) /* r 1=A/D error */ -#define DT2811_ADCSR_ADBUSY BIT(5) /* r 1=A/D busy */ -#define DT2811_ADCSR_CLRERROR BIT(4) -#define DT2811_ADCSR_DMAENB BIT(3) /* r/w 1=dma ena */ -#define DT2811_ADCSR_INTENB BIT(2) /* r/w 1=interrupts ena */ -#define DT2811_ADCSR_ADMODE(x) (((x) & 0x3) << 0) - -#define DT2811_ADGCR_REG 0x01 /* r/w A/D Gain/Channel */ -#define DT2811_ADGCR_GAIN(x) (((x) & 0x3) << 6) -#define DT2811_ADGCR_CHAN(x) (((x) & 0xf) << 0) - -#define DT2811_ADDATA_LO_REG 0x02 /* r A/D Data low byte */ -#define DT2811_ADDATA_HI_REG 0x03 /* r A/D Data high byte */ - -#define DT2811_DADATA_LO_REG(x) (0x02 + ((x) * 2)) /* w D/A Data low */ -#define DT2811_DADATA_HI_REG(x) (0x03 + ((x) * 2)) /* w D/A Data high */ - -#define DT2811_DI_REG 0x06 /* r Digital Input Port 0 */ -#define DT2811_DO_REG 0x06 /* w Digital Output Port 1 */ - -#define DT2811_TMRCTR_REG 0x07 /* r/w Timer/Counter */ -#define DT2811_TMRCTR_MANTISSA(x) (((x) & 0x7) << 3) -#define DT2811_TMRCTR_EXPONENT(x) (((x) & 0x7) << 0) - -#define DT2811_OSC_BASE 1666 /* 600 kHz = 1666.6667ns */ - -/* - * Timer frequency control: - * DT2811_TMRCTR_MANTISSA DT2811_TMRCTR_EXPONENT - * val divisor frequency val multiply divisor/divide frequency by - * 0 1 600 kHz 0 1 - * 1 10 60 kHz 1 10 - * 2 2 300 kHz 2 100 - * 3 3 200 kHz 3 1000 - * 4 4 150 kHz 4 10000 - * 5 5 120 kHz 5 100000 - * 6 6 100 kHz 6 1000000 - * 7 12 50 kHz 7 10000000 - */ -static const unsigned int dt2811_clk_dividers[] = { - 1, 10, 2, 3, 4, 5, 6, 12 -}; - -static const unsigned int dt2811_clk_multipliers[] = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 -}; - -/* - * The Analog Input range is set using jumpers on the board. - * - * Input Range W9 W10 - * -5V to +5V In Out - * -2.5V to +2.5V In In - * 0V to +5V Out In - * - * The gain may be set to 1, 2, 4, or 8 (on the dt2811-pgh) or to - * 1, 10, 100, 500 (on the dt2811-pgl). - */ -static const struct comedi_lrange dt2811_pgh_ai_ranges = { - 12, { - BIP_RANGE(5), /* range 0: gain=1 */ - BIP_RANGE(2.5), /* range 1: gain=2 */ - BIP_RANGE(1.25), /* range 2: gain=4 */ - BIP_RANGE(0.625), /* range 3: gain=8 */ - - BIP_RANGE(2.5), /* range 0+4: gain=1 */ - BIP_RANGE(1.25), /* range 1+4: gain=2 */ - BIP_RANGE(0.625), /* range 2+4: gain=4 */ - BIP_RANGE(0.3125), /* range 3+4: gain=8 */ - - UNI_RANGE(5), /* range 0+8: gain=1 */ - UNI_RANGE(2.5), /* range 1+8: gain=2 */ - UNI_RANGE(1.25), /* range 2+8: gain=4 */ - UNI_RANGE(0.625) /* range 3+8: gain=8 */ - } -}; - -static const struct comedi_lrange dt2811_pgl_ai_ranges = { - 12, { - BIP_RANGE(5), /* range 0: gain=1 */ - BIP_RANGE(0.5), /* range 1: gain=10 */ - BIP_RANGE(0.05), /* range 2: gain=100 */ - BIP_RANGE(0.01), /* range 3: gain=500 */ - - BIP_RANGE(2.5), /* range 0+4: gain=1 */ - BIP_RANGE(0.25), /* range 1+4: gain=10 */ - BIP_RANGE(0.025), /* range 2+4: gain=100 */ - BIP_RANGE(0.005), /* range 3+4: gain=500 */ - - UNI_RANGE(5), /* range 0+8: gain=1 */ - UNI_RANGE(0.5), /* range 1+8: gain=10 */ - UNI_RANGE(0.05), /* range 2+8: gain=100 */ - UNI_RANGE(0.01) /* range 3+8: gain=500 */ - } -}; - -/* - * The Analog Output range is set per-channel using jumpers on the board. - * - * DAC0 Jumpers DAC1 Jumpers - * Output Range W5 W6 W7 W8 W1 W2 W3 W4 - * -5V to +5V In Out In Out In Out In Out - * -2.5V to +2.5V In Out Out In In Out Out In - * 0 to +5V Out In Out In Out In Out In - */ -static const struct comedi_lrange dt2811_ao_ranges = { - 3, { - BIP_RANGE(5), /* default setting from factory */ - BIP_RANGE(2.5), - UNI_RANGE(5) - } -}; - -struct dt2811_board { - const char *name; - unsigned int is_pgh:1; -}; - -static const struct dt2811_board dt2811_boards[] = { - { - .name = "dt2811-pgh", - .is_pgh = 1, - }, { - .name = "dt2811-pgl", - }, -}; - -struct dt2811_private { - unsigned int ai_divisor; -}; - -static unsigned int dt2811_ai_read_sample(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - unsigned int val; - - val = inb(dev->iobase + DT2811_ADDATA_LO_REG) | - (inb(dev->iobase + DT2811_ADDATA_HI_REG) << 8); - - return val & s->maxdata; -} - -static irqreturn_t dt2811_interrupt(int irq, void *d) -{ - struct comedi_device *dev = d; - struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned int status; - - if (!dev->attached) - return IRQ_NONE; - - status = inb(dev->iobase + DT2811_ADCSR_REG); - - if (status & DT2811_ADCSR_ADERROR) { - async->events |= COMEDI_CB_OVERFLOW; - - outb(status | DT2811_ADCSR_CLRERROR, - dev->iobase + DT2811_ADCSR_REG); - } - - if (status & DT2811_ADCSR_ADDONE) { - unsigned short val; - - val = dt2811_ai_read_sample(dev, s); - comedi_buf_write_samples(s, &val, 1); - } - - if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) - async->events |= COMEDI_CB_EOA; - - comedi_handle_events(dev, s); - - return IRQ_HANDLED; -} - -static int dt2811_ai_cancel(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - /* - * Mode 0 - * Single conversion - * - * Loading a chanspec will trigger a conversion. - */ - outb(DT2811_ADCSR_ADMODE(0), dev->iobase + DT2811_ADCSR_REG); - - return 0; -} - -static void dt2811_ai_set_chanspec(struct comedi_device *dev, - unsigned int chanspec) -{ - unsigned int chan = CR_CHAN(chanspec); - unsigned int range = CR_RANGE(chanspec); - - outb(DT2811_ADGCR_CHAN(chan) | DT2811_ADGCR_GAIN(range), - dev->iobase + DT2811_ADGCR_REG); -} - -static int dt2811_ai_cmd(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct dt2811_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - unsigned int mode; - - if (cmd->start_src == TRIG_NOW) { - /* - * Mode 1 - * Continuous conversion, internal trigger and clock - * - * This resets the trigger flip-flop, disabling A/D strobes. - * The timer/counter register is loaded with the division - * ratio which will give the required sample rate. - * - * Loading the first chanspec sets the trigger flip-flop, - * enabling the timer/counter. A/D strobes are then generated - * at the rate set by the internal clock/divider. - */ - mode = DT2811_ADCSR_ADMODE(1); - } else { /* TRIG_EXT */ - if (cmd->convert_src == TRIG_TIMER) { - /* - * Mode 2 - * Continuous conversion, external trigger - * - * Similar to Mode 1, with the exception that the - * trigger flip-flop must be set by a negative edge - * on the external trigger input. - */ - mode = DT2811_ADCSR_ADMODE(2); - } else { /* TRIG_EXT */ - /* - * Mode 3 - * Continuous conversion, external trigger, clock - * - * Similar to Mode 2, with the exception that the - * conversion rate is set by the frequency on the - * external clock/divider. - */ - mode = DT2811_ADCSR_ADMODE(3); - } - } - outb(mode | DT2811_ADCSR_INTENB, dev->iobase + DT2811_ADCSR_REG); - - /* load timer */ - outb(devpriv->ai_divisor, dev->iobase + DT2811_TMRCTR_REG); - - /* load chanspec - enables timer */ - dt2811_ai_set_chanspec(dev, cmd->chanlist[0]); - - return 0; -} - -static unsigned int dt2811_ns_to_timer(unsigned int *nanosec, - unsigned int flags) -{ - unsigned long long ns; - unsigned int ns_lo = COMEDI_MIN_SPEED; - unsigned int ns_hi = 0; - unsigned int divisor_hi = 0; - unsigned int divisor_lo = 0; - unsigned int _div; - unsigned int _mult; - - /* - * Work through all the divider/multiplier values to find the two - * closest divisors to generate the requested nanosecond timing. - */ - for (_div = 0; _div <= 7; _div++) { - for (_mult = 0; _mult <= 7; _mult++) { - unsigned int div = dt2811_clk_dividers[_div]; - unsigned int mult = dt2811_clk_multipliers[_mult]; - unsigned long long divider = div * mult; - unsigned int divisor = DT2811_TMRCTR_MANTISSA(_div) | - DT2811_TMRCTR_EXPONENT(_mult); - - /* - * The timer can be configured to run at a slowest - * speed of 0.005hz (600 Khz/120000000), which requires - * 37-bits to represent the nanosecond value. Limit the - * slowest timing to what comedi handles (32-bits). - */ - ns = divider * DT2811_OSC_BASE; - if (ns > COMEDI_MIN_SPEED) - continue; - - /* Check for fastest found timing */ - if (ns <= *nanosec && ns > ns_hi) { - ns_hi = ns; - divisor_hi = divisor; - } - /* Check for slowest found timing */ - if (ns >= *nanosec && ns < ns_lo) { - ns_lo = ns; - divisor_lo = divisor; - } - } - } - - /* - * The slowest found timing will be invalid if the requested timing - * is faster than what can be generated by the timer. Fix it so that - * CMDF_ROUND_UP returns valid timing. - */ - if (ns_lo == COMEDI_MIN_SPEED) { - ns_lo = ns_hi; - divisor_lo = divisor_hi; - } - /* - * The fastest found timing will be invalid if the requested timing - * is less than what can be generated by the timer. Fix it so that - * CMDF_ROUND_NEAREST and CMDF_ROUND_DOWN return valid timing. - */ - if (ns_hi == 0) { - ns_hi = ns_lo; - divisor_hi = divisor_lo; - } - - switch (flags & CMDF_ROUND_MASK) { - case CMDF_ROUND_NEAREST: - default: - if (ns_hi - *nanosec < *nanosec - ns_lo) { - *nanosec = ns_lo; - return divisor_lo; - } - *nanosec = ns_hi; - return divisor_hi; - case CMDF_ROUND_UP: - *nanosec = ns_lo; - return divisor_lo; - case CMDF_ROUND_DOWN: - *nanosec = ns_hi; - return divisor_hi; - } -} - -static int dt2811_ai_cmdtest(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - struct dt2811_private *devpriv = dev->private; - unsigned int arg; - int err = 0; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); - err |= comedi_check_trigger_src(&cmd->convert_src, - TRIG_TIMER | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); - err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); - - if (err) - return 1; - - /* Step 2a : make sure trigger sources are unique */ - - err |= comedi_check_trigger_is_unique(cmd->start_src); - err |= comedi_check_trigger_is_unique(cmd->convert_src); - err |= comedi_check_trigger_is_unique(cmd->stop_src); - - /* Step 2b : and mutually compatible */ - - if (cmd->convert_src == TRIG_EXT && cmd->start_src != TRIG_EXT) - err |= -EINVAL; - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); - if (cmd->convert_src == TRIG_TIMER) - err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 12500); - err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, - cmd->chanlist_len); - if (cmd->stop_src == TRIG_COUNT) - err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); - else /* TRIG_NONE */ - err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); - - if (err) - return 3; - - /* Step 4: fix up any arguments */ - - if (cmd->convert_src == TRIG_TIMER) { - arg = cmd->convert_arg; - devpriv->ai_divisor = dt2811_ns_to_timer(&arg, cmd->flags); - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); - } else { /* TRIG_EXT */ - /* The convert_arg is used to set the divisor. */ - devpriv->ai_divisor = cmd->convert_arg; - } - - if (err) - return 4; - - /* Step 5: check channel list if it exists */ - - return 0; -} - -static int dt2811_ai_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int status; - - status = inb(dev->iobase + DT2811_ADCSR_REG); - if ((status & DT2811_ADCSR_ADBUSY) == 0) - return 0; - return -EBUSY; -} - -static int dt2811_ai_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int ret; - int i; - - /* We will already be in Mode 0 */ - for (i = 0; i < insn->n; i++) { - /* load chanspec and trigger conversion */ - dt2811_ai_set_chanspec(dev, insn->chanspec); - - ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0); - if (ret) - return ret; - - data[i] = dt2811_ai_read_sample(dev, s); - } - - return insn->n; -} - -static int dt2811_ao_insn_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int val = s->readback[chan]; - int i; - - for (i = 0; i < insn->n; i++) { - val = data[i]; - outb(val & 0xff, dev->iobase + DT2811_DADATA_LO_REG(chan)); - outb((val >> 8) & 0xff, - dev->iobase + DT2811_DADATA_HI_REG(chan)); - } - s->readback[chan] = val; - - return insn->n; -} - -static int dt2811_di_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - data[1] = inb(dev->iobase + DT2811_DI_REG); - - return insn->n; -} - -static int dt2811_do_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - if (comedi_dio_update_state(s, data)) - outb(s->state, dev->iobase + DT2811_DO_REG); - - data[1] = s->state; - - return insn->n; -} - -static void dt2811_reset(struct comedi_device *dev) -{ - /* This is the initialization sequence from the users manual */ - outb(DT2811_ADCSR_ADMODE(0), dev->iobase + DT2811_ADCSR_REG); - usleep_range(100, 1000); - inb(dev->iobase + DT2811_ADDATA_LO_REG); - inb(dev->iobase + DT2811_ADDATA_HI_REG); - outb(DT2811_ADCSR_ADMODE(0) | DT2811_ADCSR_CLRERROR, - dev->iobase + DT2811_ADCSR_REG); -} - -static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) -{ - const struct dt2811_board *board = dev->board_ptr; - struct dt2811_private *devpriv; - struct comedi_subdevice *s; - int ret; - - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - - ret = comedi_request_region(dev, it->options[0], 0x8); - if (ret) - return ret; - - dt2811_reset(dev); - - /* IRQ's 2,3,5,7 are valid for async command support */ - if (it->options[1] <= 7 && (BIT(it->options[1]) & 0xac)) { - ret = request_irq(it->options[1], dt2811_interrupt, 0, - dev->board_name, dev); - if (ret == 0) - dev->irq = it->options[1]; - } - - ret = comedi_alloc_subdevices(dev, 4); - if (ret) - return ret; - - /* Analog Input subdevice */ - s = &dev->subdevices[0]; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | - ((it->options[2] == 1) ? SDF_DIFF : - (it->options[2] == 2) ? SDF_COMMON : SDF_GROUND); - s->n_chan = (it->options[2] == 1) ? 8 : 16; - s->maxdata = 0x0fff; - s->range_table = board->is_pgh ? &dt2811_pgh_ai_ranges - : &dt2811_pgl_ai_ranges; - s->insn_read = dt2811_ai_insn_read; - if (dev->irq) { - dev->read_subdev = s; - s->subdev_flags |= SDF_CMD_READ; - s->len_chanlist = 1; - s->do_cmdtest = dt2811_ai_cmdtest; - s->do_cmd = dt2811_ai_cmd; - s->cancel = dt2811_ai_cancel; - } - - /* Analog Output subdevice */ - s = &dev->subdevices[1]; - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 2; - s->maxdata = 0x0fff; - s->range_table = &dt2811_ao_ranges; - s->insn_write = dt2811_ao_insn_write; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - - /* Digital Input subdevice */ - s = &dev->subdevices[2]; - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = dt2811_di_insn_bits; - - /* Digital Output subdevice */ - s = &dev->subdevices[3]; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = dt2811_do_insn_bits; - - return 0; -} - -static struct comedi_driver dt2811_driver = { - .driver_name = "dt2811", - .module = THIS_MODULE, - .attach = dt2811_attach, - .detach = comedi_legacy_detach, - .board_name = &dt2811_boards[0].name, - .num_names = ARRAY_SIZE(dt2811_boards), - .offset = sizeof(struct dt2811_board), -}; -module_comedi_driver(dt2811_driver); - -MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for Data Translation DT2811 series boards"); -MODULE_LICENSE("GPL"); |