diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/pcl711.c')
-rw-r--r-- | drivers/staging/comedi/drivers/pcl711.c | 513 |
1 files changed, 0 insertions, 513 deletions
diff --git a/drivers/staging/comedi/drivers/pcl711.c b/drivers/staging/comedi/drivers/pcl711.c deleted file mode 100644 index bd6f42fe9e3c..000000000000 --- a/drivers/staging/comedi/drivers/pcl711.c +++ /dev/null @@ -1,513 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * pcl711.c - * Comedi driver for PC-LabCard PCL-711 and AdSys ACL-8112 and compatibles - * Copyright (C) 1998 David A. Schleef <ds@schleef.org> - * Janne Jalkanen <jalkanen@cs.hut.fi> - * Eric Bunn <ebu@cs.hut.fi> - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1998 David A. Schleef <ds@schleef.org> - */ - -/* - * Driver: pcl711 - * Description: Advantech PCL-711 and 711b, ADLink ACL-8112 - * Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b), - * [ADLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg) - * Author: David A. Schleef <ds@schleef.org> - * Janne Jalkanen <jalkanen@cs.hut.fi> - * Eric Bunn <ebu@cs.hut.fi> - * Updated: - * Status: mostly complete - * - * Configuration Options: - * [0] - I/O port base - * [1] - IRQ, optional - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/interrupt.h> - -#include "../comedidev.h" - -#include "comedi_8254.h" - -/* - * I/O port register map - */ -#define PCL711_TIMER_BASE 0x00 -#define PCL711_AI_LSB_REG 0x04 -#define PCL711_AI_MSB_REG 0x05 -#define PCL711_AI_MSB_DRDY BIT(4) -#define PCL711_AO_LSB_REG(x) (0x04 + ((x) * 2)) -#define PCL711_AO_MSB_REG(x) (0x05 + ((x) * 2)) -#define PCL711_DI_LSB_REG 0x06 -#define PCL711_DI_MSB_REG 0x07 -#define PCL711_INT_STAT_REG 0x08 -#define PCL711_INT_STAT_CLR (0 << 0) /* any value will work */ -#define PCL711_AI_GAIN_REG 0x09 -#define PCL711_AI_GAIN(x) (((x) & 0xf) << 0) -#define PCL711_MUX_REG 0x0a -#define PCL711_MUX_CHAN(x) (((x) & 0xf) << 0) -#define PCL711_MUX_CS0 BIT(4) -#define PCL711_MUX_CS1 BIT(5) -#define PCL711_MUX_DIFF (PCL711_MUX_CS0 | PCL711_MUX_CS1) -#define PCL711_MODE_REG 0x0b -#define PCL711_MODE(x) (((x) & 0x7) << 0) -#define PCL711_MODE_DEFAULT PCL711_MODE(0) -#define PCL711_MODE_SOFTTRIG PCL711_MODE(1) -#define PCL711_MODE_EXT PCL711_MODE(2) -#define PCL711_MODE_EXT_IRQ PCL711_MODE(3) -#define PCL711_MODE_PACER PCL711_MODE(4) -#define PCL711_MODE_PACER_IRQ PCL711_MODE(6) -#define PCL711_MODE_IRQ(x) (((x) & 0x7) << 4) -#define PCL711_SOFTTRIG_REG 0x0c -#define PCL711_SOFTTRIG (0 << 0) /* any value will work */ -#define PCL711_DO_LSB_REG 0x0d -#define PCL711_DO_MSB_REG 0x0e - -static const struct comedi_lrange range_pcl711b_ai = { - 5, { - BIP_RANGE(5), - BIP_RANGE(2.5), - BIP_RANGE(1.25), - BIP_RANGE(0.625), - BIP_RANGE(0.3125) - } -}; - -static const struct comedi_lrange range_acl8112hg_ai = { - 12, { - BIP_RANGE(5), - BIP_RANGE(0.5), - BIP_RANGE(0.05), - BIP_RANGE(0.005), - UNI_RANGE(10), - UNI_RANGE(1), - UNI_RANGE(0.1), - UNI_RANGE(0.01), - BIP_RANGE(10), - BIP_RANGE(1), - BIP_RANGE(0.1), - BIP_RANGE(0.01) - } -}; - -static const struct comedi_lrange range_acl8112dg_ai = { - 9, { - BIP_RANGE(5), - BIP_RANGE(2.5), - BIP_RANGE(1.25), - BIP_RANGE(0.625), - UNI_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(2.5), - UNI_RANGE(1.25), - BIP_RANGE(10) - } -}; - -struct pcl711_board { - const char *name; - int n_aichan; - int n_aochan; - int maxirq; - const struct comedi_lrange *ai_range_type; -}; - -static const struct pcl711_board boardtypes[] = { - { - .name = "pcl711", - .n_aichan = 8, - .n_aochan = 1, - .ai_range_type = &range_bipolar5, - }, { - .name = "pcl711b", - .n_aichan = 8, - .n_aochan = 1, - .maxirq = 7, - .ai_range_type = &range_pcl711b_ai, - }, { - .name = "acl8112hg", - .n_aichan = 16, - .n_aochan = 2, - .maxirq = 15, - .ai_range_type = &range_acl8112hg_ai, - }, { - .name = "acl8112dg", - .n_aichan = 16, - .n_aochan = 2, - .maxirq = 15, - .ai_range_type = &range_acl8112dg_ai, - }, -}; - -static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode) -{ - /* - * The pcl711b board uses bits in the mode register to select the - * interrupt. The other boards supported by this driver all use - * jumpers on the board. - * - * Enables the interrupt when needed on the pcl711b board. These - * bits do nothing on the other boards. - */ - if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ) - mode |= PCL711_MODE_IRQ(dev->irq); - - outb(mode, dev->iobase + PCL711_MODE_REG); -} - -static unsigned int pcl711_ai_get_sample(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - unsigned int val; - - val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8; - val |= inb(dev->iobase + PCL711_AI_LSB_REG); - - return val & s->maxdata; -} - -static int pcl711_ai_cancel(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); - pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG); - return 0; -} - -static irqreturn_t pcl711_interrupt(int irq, void *d) -{ - struct comedi_device *dev = d; - struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; - unsigned short data; - - if (!dev->attached) { - dev_err(dev->class_dev, "spurious interrupt\n"); - return IRQ_HANDLED; - } - - data = pcl711_ai_get_sample(dev, s); - - outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); - - comedi_buf_write_samples(s, &data, 1); - - if (cmd->stop_src == TRIG_COUNT && - s->async->scans_done >= cmd->stop_arg) - s->async->events |= COMEDI_CB_EOA; - - comedi_handle_events(dev, s); - - return IRQ_HANDLED; -} - -static void pcl711_set_changain(struct comedi_device *dev, - struct comedi_subdevice *s, - unsigned int chanspec) -{ - unsigned int chan = CR_CHAN(chanspec); - unsigned int range = CR_RANGE(chanspec); - unsigned int aref = CR_AREF(chanspec); - unsigned int mux = 0; - - outb(PCL711_AI_GAIN(range), dev->iobase + PCL711_AI_GAIN_REG); - - if (s->n_chan > 8) { - /* Select the correct MPC508A chip */ - if (aref == AREF_DIFF) { - chan &= 0x7; - mux |= PCL711_MUX_DIFF; - } else { - if (chan < 8) - mux |= PCL711_MUX_CS0; - else - mux |= PCL711_MUX_CS1; - } - } - outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG); -} - -static int pcl711_ai_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int status; - - status = inb(dev->iobase + PCL711_AI_MSB_REG); - if ((status & PCL711_AI_MSB_DRDY) == 0) - return 0; - return -EBUSY; -} - -static int pcl711_ai_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int ret; - int i; - - pcl711_set_changain(dev, s, insn->chanspec); - - pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG); - - for (i = 0; i < insn->n; i++) { - outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG); - - ret = comedi_timeout(dev, s, insn, pcl711_ai_eoc, 0); - if (ret) - return ret; - - data[i] = pcl711_ai_get_sample(dev, s); - } - - return insn->n; -} - -static int pcl711_ai_cmdtest(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_cmd *cmd) -{ - int err = 0; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, - TRIG_TIMER | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); - 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->scan_begin_src); - err |= comedi_check_trigger_is_unique(cmd->stop_src); - - /* Step 2b : and mutually compatible */ - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - - if (cmd->scan_begin_src == TRIG_EXT) { - err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); - } else { -#define MAX_SPEED 1000 - err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, - MAX_SPEED); - } - - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); - 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 */ - - if (cmd->scan_begin_src == TRIG_TIMER) { - unsigned int arg = cmd->scan_begin_arg; - - comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); - err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); - } - - if (err) - return 4; - - return 0; -} - -static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct comedi_cmd *cmd = &s->async->cmd; - - pcl711_set_changain(dev, s, cmd->chanlist[0]); - - if (cmd->scan_begin_src == TRIG_TIMER) { - comedi_8254_update_divisors(dev->pacer); - comedi_8254_pacer_enable(dev->pacer, 1, 2, true); - outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); - pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ); - } else { - pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ); - } - - return 0; -} - -static void pcl711_ao_write(struct comedi_device *dev, - unsigned int chan, unsigned int val) -{ - outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan)); - outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan)); -} - -static int pcl711_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]; - pcl711_ao_write(dev, chan, val); - } - s->readback[chan] = val; - - return insn->n; -} - -static int pcl711_di_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int val; - - val = inb(dev->iobase + PCL711_DI_LSB_REG); - val |= (inb(dev->iobase + PCL711_DI_MSB_REG) << 8); - - data[1] = val; - - return insn->n; -} - -static int pcl711_do_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int mask; - - mask = comedi_dio_update_state(s, data); - if (mask) { - if (mask & 0x00ff) - outb(s->state & 0xff, dev->iobase + PCL711_DO_LSB_REG); - if (mask & 0xff00) - outb((s->state >> 8), dev->iobase + PCL711_DO_MSB_REG); - } - - data[1] = s->state; - - return insn->n; -} - -static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it) -{ - const struct pcl711_board *board = dev->board_ptr; - struct comedi_subdevice *s; - int ret; - - ret = comedi_request_region(dev, it->options[0], 0x10); - if (ret) - return ret; - - if (it->options[1] && it->options[1] <= board->maxirq) { - ret = request_irq(it->options[1], pcl711_interrupt, 0, - dev->board_name, dev); - if (ret == 0) - dev->irq = it->options[1]; - } - - dev->pacer = comedi_8254_init(dev->iobase + PCL711_TIMER_BASE, - I8254_OSC_BASE_2MHZ, I8254_IO8, 0); - if (!dev->pacer) - return -ENOMEM; - - 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 | SDF_GROUND; - if (board->n_aichan > 8) - s->subdev_flags |= SDF_DIFF; - s->n_chan = board->n_aichan; - s->maxdata = 0xfff; - s->range_table = board->ai_range_type; - s->insn_read = pcl711_ai_insn_read; - if (dev->irq) { - dev->read_subdev = s; - s->subdev_flags |= SDF_CMD_READ; - s->len_chanlist = 1; - s->do_cmdtest = pcl711_ai_cmdtest; - s->do_cmd = pcl711_ai_cmd; - s->cancel = pcl711_ai_cancel; - } - - /* Analog Output subdevice */ - s = &dev->subdevices[1]; - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = board->n_aochan; - s->maxdata = 0xfff; - s->range_table = &range_bipolar5; - s->insn_write = pcl711_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 = 16; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = pcl711_di_insn_bits; - - /* Digital Output subdevice */ - s = &dev->subdevices[3]; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 16; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = pcl711_do_insn_bits; - - /* clear DAC */ - pcl711_ao_write(dev, 0, 0x0); - pcl711_ao_write(dev, 1, 0x0); - - return 0; -} - -static struct comedi_driver pcl711_driver = { - .driver_name = "pcl711", - .module = THIS_MODULE, - .attach = pcl711_attach, - .detach = comedi_legacy_detach, - .board_name = &boardtypes[0].name, - .num_names = ARRAY_SIZE(boardtypes), - .offset = sizeof(struct pcl711_board), -}; -module_comedi_driver(pcl711_driver); - -MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for PCL-711 compatible boards"); -MODULE_LICENSE("GPL"); |