diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/rti800.c')
-rw-r--r-- | drivers/staging/comedi/drivers/rti800.c | 575 |
1 files changed, 249 insertions, 326 deletions
diff --git a/drivers/staging/comedi/drivers/rti800.c b/drivers/staging/comedi/drivers/rti800.c index 7e577e44490..f4163fd35a0 100644 --- a/drivers/staging/comedi/drivers/rti800.c +++ b/drivers/staging/comedi/drivers/rti800.c @@ -1,186 +1,184 @@ /* - comedi/drivers/rti800.c - Hardware driver for Analog Devices RTI-800/815 board - - COMEDI - Linux Control and Measurement Device Interface - Copyright (C) 1998 David A. Schleef <ds@schleef.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - + * comedi/drivers/rti800.c + * Hardware driver for Analog Devices RTI-800/815 board + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1998 David A. Schleef <ds@schleef.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* -Driver: rti800 -Description: Analog Devices RTI-800/815 -Author: ds -Status: unknown -Updated: Fri, 05 Sep 2008 14:50:44 +0100 -Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815) - -Configuration options: - [0] - I/O port base address - [1] - IRQ - [2] - A/D reference - 0 = differential - 1 = pseudodifferential (common) - 2 = single-ended - [3] - A/D range - 0 = [-10,10] - 1 = [-5,5] - 2 = [0,10] - [4] - A/D encoding - 0 = two's complement - 1 = straight binary - [5] - DAC 0 range - 0 = [-10,10] - 1 = [0,10] - [6] - DAC 0 encoding - 0 = two's complement - 1 = straight binary - [7] - DAC 1 range (same as DAC 0) - [8] - DAC 1 encoding (same as DAC 0) -*/ + * Driver: rti800 + * Description: Analog Devices RTI-800/815 + * Devices: (Analog Devices) RTI-800 [rti800] + * (Analog Devices) RTI-815 [rti815] + * Author: David A. Schleef <ds@schleef.org> + * Status: unknown + * Updated: Fri, 05 Sep 2008 14:50:44 +0100 + * + * Configuration options: + * [0] - I/O port base address + * [1] - IRQ (not supported / unused) + * [2] - A/D mux/reference (number of channels) + * 0 = differential + * 1 = pseudodifferential (common) + * 2 = single-ended + * [3] - A/D range + * 0 = [-10,10] + * 1 = [-5,5] + * 2 = [0,10] + * [4] - A/D encoding + * 0 = two's complement + * 1 = straight binary + * [5] - DAC 0 range + * 0 = [-10,10] + * 1 = [0,10] + * [6] - DAC 0 encoding + * 0 = two's complement + * 1 = straight binary + * [7] - DAC 1 range (same as DAC 0) + * [8] - DAC 1 encoding (same as DAC 0) + */ #include <linux/interrupt.h> #include "../comedidev.h" #include <linux/ioport.h> -#define RTI800_SIZE 16 - -#define RTI800_CSR 0 -#define RTI800_MUXGAIN 1 -#define RTI800_CONVERT 2 -#define RTI800_ADCLO 3 -#define RTI800_ADCHI 4 -#define RTI800_DAC0LO 5 -#define RTI800_DAC0HI 6 -#define RTI800_DAC1LO 7 -#define RTI800_DAC1HI 8 -#define RTI800_CLRFLAGS 9 -#define RTI800_DI 10 -#define RTI800_DO 11 -#define RTI800_9513A_DATA 12 -#define RTI800_9513A_CNTRL 13 -#define RTI800_9513A_STATUS 13 - /* - * flags for CSR register + * Register map */ +#define RTI800_CSR 0x00 +#define RTI800_CSR_BUSY (1 << 7) +#define RTI800_CSR_DONE (1 << 6) +#define RTI800_CSR_OVERRUN (1 << 5) +#define RTI800_CSR_TCR (1 << 4) +#define RTI800_CSR_DMA_ENAB (1 << 3) +#define RTI800_CSR_INTR_TC (1 << 2) +#define RTI800_CSR_INTR_EC (1 << 1) +#define RTI800_CSR_INTR_OVRN (1 << 0) +#define RTI800_MUXGAIN 0x01 +#define RTI800_CONVERT 0x02 +#define RTI800_ADCLO 0x03 +#define RTI800_ADCHI 0x04 +#define RTI800_DAC0LO 0x05 +#define RTI800_DAC0HI 0x06 +#define RTI800_DAC1LO 0x07 +#define RTI800_DAC1HI 0x08 +#define RTI800_CLRFLAGS 0x09 +#define RTI800_DI 0x0a +#define RTI800_DO 0x0b +#define RTI800_9513A_DATA 0x0c +#define RTI800_9513A_CNTRL 0x0d +#define RTI800_9513A_STATUS 0x0d + +#define RTI800_IOSIZE 0x10 + +#define RTI800_AI_TIMEOUT 100 + +static const struct comedi_lrange range_rti800_ai_10_bipolar = { + 4, { + BIP_RANGE(10), + BIP_RANGE(1), + BIP_RANGE(0.1), + BIP_RANGE(0.02) + } +}; -#define RTI800_BUSY 0x80 -#define RTI800_DONE 0x40 -#define RTI800_OVERRUN 0x20 -#define RTI800_TCR 0x10 -#define RTI800_DMA_ENAB 0x08 -#define RTI800_INTR_TC 0x04 -#define RTI800_INTR_EC 0x02 -#define RTI800_INTR_OVRN 0x01 - -#define Am9513_8BITBUS - -#define Am9513_output_control(a) outb(a, dev->iobase+RTI800_9513A_CNTRL) -#define Am9513_output_data(a) outb(a, dev->iobase+RTI800_9513A_DATA) -#define Am9513_input_data() inb(dev->iobase+RTI800_9513A_DATA) -#define Am9513_input_status() inb(dev->iobase+RTI800_9513A_STATUS) - -#include "am9513.h" - -static const struct comedi_lrange range_rti800_ai_10_bipolar = { 4, { - BIP_RANGE - (10), - BIP_RANGE - (1), - BIP_RANGE - (0.1), - BIP_RANGE - (0.02) - } +static const struct comedi_lrange range_rti800_ai_5_bipolar = { + 4, { + BIP_RANGE(5), + BIP_RANGE(0.5), + BIP_RANGE(0.05), + BIP_RANGE(0.01) + } }; -static const struct comedi_lrange range_rti800_ai_5_bipolar = { 4, { - BIP_RANGE - (5), - BIP_RANGE - (0.5), - BIP_RANGE - (0.05), - BIP_RANGE - (0.01) - } +static const struct comedi_lrange range_rti800_ai_unipolar = { + 4, { + UNI_RANGE(10), + UNI_RANGE(1), + UNI_RANGE(0.1), + UNI_RANGE(0.02) + } }; -static const struct comedi_lrange range_rti800_ai_unipolar = { 4, { - UNI_RANGE - (10), - UNI_RANGE(1), - UNI_RANGE - (0.1), - UNI_RANGE - (0.02) - } +static const struct comedi_lrange *const rti800_ai_ranges[] = { + &range_rti800_ai_10_bipolar, + &range_rti800_ai_5_bipolar, + &range_rti800_ai_unipolar, }; -struct rti800_board { +static const struct comedi_lrange *const rti800_ao_ranges[] = { + &range_bipolar10, + &range_unipolar10, +}; +struct rti800_board { const char *name; int has_ao; }; -static irqreturn_t rti800_interrupt(int irq, void *dev); +static const struct rti800_board rti800_boardtypes[] = { + { + .name = "rti800", + }, { + .name = "rti815", + .has_ao = 1, + }, +}; struct rti800_private { - enum { - adc_diff, adc_pseudodiff, adc_singleended - } adc_mux; - enum { - adc_bipolar10, adc_bipolar5, adc_unipolar10 - } adc_range; - enum { - adc_2comp, adc_straight - } adc_coding; - enum { - dac_bipolar10, dac_unipolar10 - } dac0_range, dac1_range; - enum { - dac_2comp, dac_straight - } dac0_coding, dac1_coding; + bool adc_2comp; + bool dac_2comp[2]; const struct comedi_lrange *ao_range_type_list[2]; unsigned int ao_readback[2]; - int muxgain_bits; + unsigned char muxgain_bits; }; -#define RTI800_TIMEOUT 100 - -static irqreturn_t rti800_interrupt(int irq, void *dev) +static int rti800_ai_wait_for_conversion(struct comedi_device *dev, + int timeout) { - return IRQ_HANDLED; -} + unsigned char status; + int i; -/* settling delay times in usec for different gains */ -static const int gaindelay[] = { 10, 20, 40, 80 }; + for (i = 0; i < timeout; i++) { + status = inb(dev->iobase + RTI800_CSR); + if (status & RTI800_CSR_OVERRUN) { + outb(0, dev->iobase + RTI800_CLRFLAGS); + return -EIO; + } + if (status & RTI800_CSR_DONE) + return 0; + udelay(1); + } + return -ETIME; +} static int rti800_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { struct rti800_private *devpriv = dev->private; - int i, t; - int status; - int chan = CR_CHAN(insn->chanspec); - unsigned gain = CR_RANGE(insn->chanspec); - unsigned muxgain_bits; + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int gain = CR_RANGE(insn->chanspec); + unsigned char muxgain_bits; + int ret; + int i; inb(dev->iobase + RTI800_ADCHI); outb(0, dev->iobase + RTI800_CLRFLAGS); @@ -189,80 +187,80 @@ static int rti800_ai_insn_read(struct comedi_device *dev, if (muxgain_bits != devpriv->muxgain_bits) { devpriv->muxgain_bits = muxgain_bits; outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN); - /* without a delay here, the RTI_OVERRUN bit - * gets set, and you will have an error. */ + /* + * Without a delay here, the RTI_CSR_OVERRUN bit + * gets set, and you will have an error. + */ if (insn->n > 0) { - BUG_ON(gain >= ARRAY_SIZE(gaindelay)); - udelay(gaindelay[gain]); + int delay = (gain == 0) ? 10 : + (gain == 1) ? 20 : + (gain == 2) ? 40 : 80; + + udelay(delay); } } for (i = 0; i < insn->n; i++) { outb(0, dev->iobase + RTI800_CONVERT); - for (t = RTI800_TIMEOUT; t; t--) { - status = inb(dev->iobase + RTI800_CSR); - if (status & RTI800_OVERRUN) { - printk(KERN_WARNING "rti800: a/d overrun\n"); - outb(0, dev->iobase + RTI800_CLRFLAGS); - return -EIO; - } - if (status & RTI800_DONE) - break; - udelay(1); - } - if (t == 0) { - printk(KERN_WARNING "rti800: timeout\n"); - return -ETIME; - } + ret = rti800_ai_wait_for_conversion(dev, RTI800_AI_TIMEOUT); + if (ret) + return ret; + data[i] = inb(dev->iobase + RTI800_ADCLO); - data[i] |= (0xf & inb(dev->iobase + RTI800_ADCHI)) << 8; + data[i] |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8; - if (devpriv->adc_coding == adc_2comp) + if (devpriv->adc_2comp) data[i] ^= 0x800; } - return i; + return insn->n; } static int rti800_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { struct rti800_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); int i; - int chan = CR_CHAN(insn->chanspec); for (i = 0; i < insn->n; i++) data[i] = devpriv->ao_readback[chan]; - return i; + return insn->n; } static int rti800_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { struct rti800_private *devpriv = dev->private; - int chan = CR_CHAN(insn->chanspec); - int d; + unsigned int chan = CR_CHAN(insn->chanspec); + int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO; + int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI; + int val = devpriv->ao_readback[chan]; int i; for (i = 0; i < insn->n; i++) { - devpriv->ao_readback[chan] = d = data[i]; - if (devpriv->dac0_coding == dac_2comp) - d ^= 0x800; - - outb(d & 0xff, - dev->iobase + (chan ? RTI800_DAC1LO : RTI800_DAC0LO)); - outb(d >> 8, - dev->iobase + (chan ? RTI800_DAC1HI : RTI800_DAC0HI)); + val = data[i]; + if (devpriv->dac_2comp[chan]) + val ^= 0x800; + + outb(val & 0xff, dev->iobase + reg_lo); + outb((val >> 8) & 0xff, dev->iobase + reg_hi); } - return i; + + devpriv->ao_readback[chan] = val; + + return insn->n; } static int rti800_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { data[1] = inb(dev->iobase + RTI800_DI); return insn->n; @@ -270,11 +268,16 @@ static int rti800_di_insn_bits(struct comedi_device *dev, static int rti800_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - if (data[0]) { - s->state &= ~data[0]; - s->state |= data[0] & data[1]; + unsigned int mask = data[0]; + unsigned int bits = data[1]; + + if (mask) { + s->state &= ~mask; + s->state |= (bits & mask); + /* Outputs are inverted... */ outb(s->state ^ 0xff, dev->iobase + RTI800_DO); } @@ -284,186 +287,106 @@ static int rti800_do_insn_bits(struct comedi_device *dev, return insn->n; } -/* - options[0] - I/O port - options[1] - irq - options[2] - a/d mux - 0=differential, 1=pseudodiff, 2=single - options[3] - a/d range - 0=bipolar10, 1=bipolar5, 2=unipolar10 - options[4] - a/d coding - 0=2's comp, 1=straight binary - options[5] - dac0 range - 0=bipolar10, 1=unipolar10 - options[6] - dac0 coding - 0=2's comp, 1=straight binary - options[7] - dac1 range - options[8] - dac1 coding - */ - static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it) { const struct rti800_board *board = comedi_board(dev); struct rti800_private *devpriv; - unsigned int irq; - unsigned long iobase; - int ret; struct comedi_subdevice *s; + int ret; - iobase = it->options[0]; - printk(KERN_INFO "comedi%d: rti800: 0x%04lx\n", dev->minor, iobase); - if (!request_region(iobase, RTI800_SIZE, "rti800")) { - printk(KERN_WARNING "I/O port conflict\n"); - return -EIO; - } - dev->iobase = iobase; - -#ifdef DEBUG - printk(KERN_DEBUG "fingerprint=%x,%x,%x,%x,%x ", - inb(dev->iobase + 0), - inb(dev->iobase + 1), - inb(dev->iobase + 2), - inb(dev->iobase + 3), inb(dev->iobase + 4)); -#endif + ret = comedi_request_region(dev, it->options[0], RTI800_IOSIZE); + if (ret) + return ret; outb(0, dev->iobase + RTI800_CSR); inb(dev->iobase + RTI800_ADCHI); outb(0, dev->iobase + RTI800_CLRFLAGS); - irq = it->options[1]; - if (irq) { - printk(KERN_INFO "( irq = %u )\n", irq); - ret = request_irq(irq, rti800_interrupt, 0, "rti800", dev); - if (ret < 0) { - printk(KERN_WARNING " Failed to allocate IRQ\n"); - return ret; - } - dev->irq = irq; - } else { - printk(KERN_INFO "( no irq )\n"); - } - - dev->board_name = board->name; - - ret = comedi_alloc_subdevices(dev, 4); - if (ret) - return ret; - devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); if (!devpriv) return -ENOMEM; dev->private = devpriv; - devpriv->adc_mux = it->options[2]; - devpriv->adc_range = it->options[3]; - devpriv->adc_coding = it->options[4]; - devpriv->dac0_range = it->options[5]; - devpriv->dac0_coding = it->options[6]; - devpriv->dac1_range = it->options[7]; - devpriv->dac1_coding = it->options[8]; - devpriv->muxgain_bits = -1; + devpriv->adc_2comp = (it->options[4] == 0); + devpriv->dac_2comp[0] = (it->options[6] == 0); + devpriv->dac_2comp[1] = (it->options[8] == 0); + /* invalid, forces the MUXGAIN register to be set when first used */ + devpriv->muxgain_bits = 0xff; + + ret = comedi_alloc_subdevices(dev, 4); + if (ret) + return ret; s = &dev->subdevices[0]; /* ai subdevice */ - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND; - s->n_chan = (devpriv->adc_mux ? 16 : 8); - s->insn_read = rti800_ai_insn_read; - s->maxdata = 0xfff; - switch (devpriv->adc_range) { - case adc_bipolar10: - s->range_table = &range_rti800_ai_10_bipolar; - break; - case adc_bipolar5: - s->range_table = &range_rti800_ai_5_bipolar; - break; - case adc_unipolar10: - s->range_table = &range_rti800_ai_unipolar; - break; - } + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND; + s->n_chan = (it->options[2] ? 16 : 8); + s->insn_read = rti800_ai_insn_read; + s->maxdata = 0x0fff; + s->range_table = (it->options[3] < ARRAY_SIZE(rti800_ai_ranges)) + ? rti800_ai_ranges[it->options[3]] + : &range_unknown; s = &dev->subdevices[1]; if (board->has_ao) { /* ao subdevice (only on rti815) */ - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 2; - s->insn_read = rti800_ao_insn_read; - s->insn_write = rti800_ao_insn_write; - s->maxdata = 0xfff; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = 2; + s->insn_read = rti800_ao_insn_read; + s->insn_write = rti800_ao_insn_write; + s->maxdata = 0x0fff; s->range_table_list = devpriv->ao_range_type_list; - switch (devpriv->dac0_range) { - case dac_bipolar10: - devpriv->ao_range_type_list[0] = &range_bipolar10; - break; - case dac_unipolar10: - devpriv->ao_range_type_list[0] = &range_unipolar10; - break; - } - switch (devpriv->dac1_range) { - case dac_bipolar10: - devpriv->ao_range_type_list[1] = &range_bipolar10; - break; - case dac_unipolar10: - devpriv->ao_range_type_list[1] = &range_unipolar10; - break; - } + devpriv->ao_range_type_list[0] = + (it->options[5] < ARRAY_SIZE(rti800_ao_ranges)) + ? rti800_ao_ranges[it->options[5]] + : &range_unknown; + devpriv->ao_range_type_list[1] = + (it->options[7] < ARRAY_SIZE(rti800_ao_ranges)) + ? rti800_ao_ranges[it->options[7]] + : &range_unknown; } else { - s->type = COMEDI_SUBD_UNUSED; + s->type = COMEDI_SUBD_UNUSED; } s = &dev->subdevices[2]; /* di */ - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 8; - s->insn_bits = rti800_di_insn_bits; - s->maxdata = 1; - s->range_table = &range_digital; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->insn_bits = rti800_di_insn_bits; + s->maxdata = 1; + s->range_table = &range_digital; s = &dev->subdevices[3]; /* do */ - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 8; - s->insn_bits = rti800_do_insn_bits; - s->maxdata = 1; - s->range_table = &range_digital; - -/* don't yet know how to deal with counter/timers */ -#if 0 - s = &dev->subdevices[4]; - /* do */ - s->type = COMEDI_SUBD_TIMER; -#endif + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = 8; + s->insn_bits = rti800_do_insn_bits; + s->maxdata = 1; + s->range_table = &range_digital; + + /* + * There is also an Am9513 timer on these boards. This subdevice + * is not currently supported. + */ return 0; } -static void rti800_detach(struct comedi_device *dev) -{ - if (dev->iobase) - release_region(dev->iobase, RTI800_SIZE); - if (dev->irq) - free_irq(dev->irq, dev); -} - -static const struct rti800_board boardtypes[] = { - { "rti800", 0 }, - { "rti815", 1 }, -}; - static struct comedi_driver rti800_driver = { .driver_name = "rti800", .module = THIS_MODULE, .attach = rti800_attach, - .detach = rti800_detach, - .num_names = ARRAY_SIZE(boardtypes), - .board_name = &boardtypes[0].name, + .detach = comedi_legacy_detach, + .num_names = ARRAY_SIZE(rti800_boardtypes), + .board_name = &rti800_boardtypes[0].name, .offset = sizeof(struct rti800_board), }; module_comedi_driver(rti800_driver); +MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board"); MODULE_AUTHOR("Comedi http://www.comedi.org"); -MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL"); |