Files
linux/drivers/comedi/drivers/rti800.c
Greg Kroah-Hartman 8ffdff6a8c staging: comedi: move out of staging directory
The comedi code came into the kernel back in 2008, but traces its
lifetime to much much earlier.  It's been polished and buffed and
there's really nothing preventing it from being part of the "real"
portion of the kernel.

So move it to drivers/comedi/ as it belongs there.

Many thanks to the hundreds of developers who did the work to make this
happen.

Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
Link: https://lore.kernel.org/r/YHauop4u3sP6lz8j@kroah.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-04-15 09:26:25 +02:00

358 lines
8.4 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* 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>
*/
/*
* Driver: rti800
* Description: Analog Devices RTI-800/815
* Devices: [Analog Devices] RTI-800 (rti800), 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/module.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include "../comedidev.h"
/*
* Register map
*/
#define RTI800_CSR 0x00
#define RTI800_CSR_BUSY BIT(7)
#define RTI800_CSR_DONE BIT(6)
#define RTI800_CSR_OVERRUN BIT(5)
#define RTI800_CSR_TCR BIT(4)
#define RTI800_CSR_DMA_ENAB BIT(3)
#define RTI800_CSR_INTR_TC BIT(2)
#define RTI800_CSR_INTR_EC BIT(1)
#define RTI800_CSR_INTR_OVRN BIT(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
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_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,
};
static const struct comedi_lrange *const rti800_ao_ranges[] = {
&range_bipolar10,
&range_unipolar10,
};
struct rti800_board {
const char *name;
int has_ao;
};
static const struct rti800_board rti800_boardtypes[] = {
{
.name = "rti800",
}, {
.name = "rti815",
.has_ao = 1,
},
};
struct rti800_private {
bool adc_2comp;
bool dac_2comp[2];
const struct comedi_lrange *ao_range_type_list[2];
unsigned char muxgain_bits;
};
static int rti800_ai_eoc(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned long context)
{
unsigned char status;
status = inb(dev->iobase + RTI800_CSR);
if (status & RTI800_CSR_OVERRUN) {
outb(0, dev->iobase + RTI800_CLRFLAGS);
return -EOVERFLOW;
}
if (status & RTI800_CSR_DONE)
return 0;
return -EBUSY;
}
static int rti800_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct rti800_private *devpriv = dev->private;
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);
muxgain_bits = chan | (gain << 5);
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_CSR_OVERRUN bit
* gets set, and you will have an error.
*/
if (insn->n > 0) {
int delay = (gain == 0) ? 10 :
(gain == 1) ? 20 :
(gain == 2) ? 40 : 80;
udelay(delay);
}
}
for (i = 0; i < insn->n; i++) {
unsigned int val;
outb(0, dev->iobase + RTI800_CONVERT);
ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0);
if (ret)
return ret;
val = inb(dev->iobase + RTI800_ADCLO);
val |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8;
if (devpriv->adc_2comp)
val = comedi_offset_munge(s, val);
data[i] = val;
}
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 rti800_private *devpriv = dev->private;
unsigned int chan = CR_CHAN(insn->chanspec);
int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO;
int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI;
int i;
for (i = 0; i < insn->n; i++) {
unsigned int val = data[i];
s->readback[chan] = val;
if (devpriv->dac_2comp[chan])
val = comedi_offset_munge(s, val);
outb(val & 0xff, dev->iobase + reg_lo);
outb((val >> 8) & 0xff, dev->iobase + reg_hi);
}
return insn->n;
}
static int rti800_di_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
data[1] = inb(dev->iobase + RTI800_DI);
return insn->n;
}
static int rti800_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)) {
/* Outputs are inverted... */
outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
}
data[1] = s->state;
return insn->n;
}
static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
const struct rti800_board *board = dev->board_ptr;
struct rti800_private *devpriv;
struct comedi_subdevice *s;
int ret;
ret = comedi_request_region(dev, it->options[0], 0x10);
if (ret)
return ret;
outb(0, dev->iobase + RTI800_CSR);
inb(dev->iobase + RTI800_ADCHI);
outb(0, dev->iobase + RTI800_CLRFLAGS);
devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
if (!devpriv)
return -ENOMEM;
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 = (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->maxdata = 0x0fff;
s->range_table_list = devpriv->ao_range_type_list;
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;
s->insn_write = rti800_ao_insn_write;
ret = comedi_alloc_subdev_readback(s);
if (ret)
return ret;
} else {
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 = &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;
/*
* There is also an Am9513 timer on these boards. This subdevice
* is not currently supported.
*/
return 0;
}
static struct comedi_driver rti800_driver = {
.driver_name = "rti800",
.module = THIS_MODULE,
.attach = rti800_attach,
.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 https://www.comedi.org");
MODULE_LICENSE("GPL");