summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/pcmmio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/pcmmio.c')
-rw-r--r--drivers/staging/comedi/drivers/pcmmio.c777
1 files changed, 0 insertions, 777 deletions
diff --git a/drivers/staging/comedi/drivers/pcmmio.c b/drivers/staging/comedi/drivers/pcmmio.c
deleted file mode 100644
index 24a9568d3378..000000000000
--- a/drivers/staging/comedi/drivers/pcmmio.c
+++ /dev/null
@@ -1,777 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * pcmmio.c
- * Driver for Winsystems PC-104 based multifunction IO board.
- *
- * COMEDI - Linux Control and Measurement Device Interface
- * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
- */
-
-/*
- * Driver: pcmmio
- * Description: A driver for the PCM-MIO multifunction board
- * Devices: [Winsystems] PCM-MIO (pcmmio)
- * Author: Calin Culianu <calin@ajvar.org>
- * Updated: Wed, May 16 2007 16:21:10 -0500
- * Status: works
- *
- * A driver for the PCM-MIO multifunction board from Winsystems. This
- * is a PC-104 based I/O board. It contains four subdevices:
- *
- * subdevice 0 - 16 channels of 16-bit AI
- * subdevice 1 - 8 channels of 16-bit AO
- * subdevice 2 - first 24 channels of the 48 channel of DIO
- * (with edge-triggered interrupt support)
- * subdevice 3 - last 24 channels of the 48 channel DIO
- * (no interrupt support for this bank of channels)
- *
- * Some notes:
- *
- * Synchronous reads and writes are the only things implemented for analog
- * input and output. The hardware itself can do streaming acquisition, etc.
- *
- * Asynchronous I/O for the DIO subdevices *is* implemented, however! They
- * are basically edge-triggered interrupts for any configuration of the
- * channels in subdevice 2.
- *
- * Also note that this interrupt support is untested.
- *
- * A few words about edge-detection IRQ support (commands on DIO):
- *
- * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
- * of the board to the comedi_config command. The board IRQ is not jumpered
- * but rather configured through software, so any IRQ from 1-15 is OK.
- *
- * Due to the genericity of the comedi API, you need to create a special
- * comedi_command in order to use edge-triggered interrupts for DIO.
- *
- * Use comedi_commands with TRIG_NOW. Your callback will be called each
- * time an edge is detected on the specified DIO line(s), and the data
- * values will be two sample_t's, which should be concatenated to form
- * one 32-bit unsigned int. This value is the mask of channels that had
- * edges detected from your channel list. Note that the bits positions
- * in the mask correspond to positions in your chanlist when you
- * specified the command and *not* channel id's!
- *
- * To set the polarity of the edge-detection interrupts pass a nonzero value
- * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
- * value for both CR_RANGE and CR_AREF if you want edge-down polarity.
- *
- * Configuration Options:
- * [0] - I/O port base address
- * [1] - IRQ (optional -- for edge-detect interrupt support only,
- * leave out if you don't need this feature)
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-
-#include "../comedidev.h"
-
-/*
- * Register I/O map
- */
-#define PCMMIO_AI_LSB_REG 0x00
-#define PCMMIO_AI_MSB_REG 0x01
-#define PCMMIO_AI_CMD_REG 0x02
-#define PCMMIO_AI_CMD_SE BIT(7)
-#define PCMMIO_AI_CMD_ODD_CHAN BIT(6)
-#define PCMMIO_AI_CMD_CHAN_SEL(x) (((x) & 0x3) << 4)
-#define PCMMIO_AI_CMD_RANGE(x) (((x) & 0x3) << 2)
-#define PCMMIO_RESOURCE_REG 0x02
-#define PCMMIO_RESOURCE_IRQ(x) (((x) & 0xf) << 0)
-#define PCMMIO_AI_STATUS_REG 0x03
-#define PCMMIO_AI_STATUS_DATA_READY BIT(7)
-#define PCMMIO_AI_STATUS_DATA_DMA_PEND BIT(6)
-#define PCMMIO_AI_STATUS_CMD_DMA_PEND BIT(5)
-#define PCMMIO_AI_STATUS_IRQ_PEND BIT(4)
-#define PCMMIO_AI_STATUS_DATA_DRQ_ENA BIT(2)
-#define PCMMIO_AI_STATUS_REG_SEL BIT(3)
-#define PCMMIO_AI_STATUS_CMD_DRQ_ENA BIT(1)
-#define PCMMIO_AI_STATUS_IRQ_ENA BIT(0)
-#define PCMMIO_AI_RES_ENA_REG 0x03
-#define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS (0 << 3)
-#define PCMMIO_AI_RES_ENA_AI_RES_ACCESS BIT(3)
-#define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS BIT(4)
-#define PCMMIO_AI_2ND_ADC_OFFSET 0x04
-
-#define PCMMIO_AO_LSB_REG 0x08
-#define PCMMIO_AO_LSB_SPAN(x) (((x) & 0xf) << 0)
-#define PCMMIO_AO_MSB_REG 0x09
-#define PCMMIO_AO_CMD_REG 0x0a
-#define PCMMIO_AO_CMD_WR_SPAN (0x2 << 4)
-#define PCMMIO_AO_CMD_WR_CODE (0x3 << 4)
-#define PCMMIO_AO_CMD_UPDATE (0x4 << 4)
-#define PCMMIO_AO_CMD_UPDATE_ALL (0x5 << 4)
-#define PCMMIO_AO_CMD_WR_SPAN_UPDATE (0x6 << 4)
-#define PCMMIO_AO_CMD_WR_CODE_UPDATE (0x7 << 4)
-#define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL (0x8 << 4)
-#define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL (0x9 << 4)
-#define PCMMIO_AO_CMD_RD_B1_SPAN (0xa << 4)
-#define PCMMIO_AO_CMD_RD_B1_CODE (0xb << 4)
-#define PCMMIO_AO_CMD_RD_B2_SPAN (0xc << 4)
-#define PCMMIO_AO_CMD_RD_B2_CODE (0xd << 4)
-#define PCMMIO_AO_CMD_NOP (0xf << 4)
-#define PCMMIO_AO_CMD_CHAN_SEL(x) (((x) & 0x03) << 1)
-#define PCMMIO_AO_CMD_CHAN_SEL_ALL (0x0f << 0)
-#define PCMMIO_AO_STATUS_REG 0x0b
-#define PCMMIO_AO_STATUS_DATA_READY BIT(7)
-#define PCMMIO_AO_STATUS_DATA_DMA_PEND BIT(6)
-#define PCMMIO_AO_STATUS_CMD_DMA_PEND BIT(5)
-#define PCMMIO_AO_STATUS_IRQ_PEND BIT(4)
-#define PCMMIO_AO_STATUS_DATA_DRQ_ENA BIT(2)
-#define PCMMIO_AO_STATUS_REG_SEL BIT(3)
-#define PCMMIO_AO_STATUS_CMD_DRQ_ENA BIT(1)
-#define PCMMIO_AO_STATUS_IRQ_ENA BIT(0)
-#define PCMMIO_AO_RESOURCE_ENA_REG 0x0b
-#define PCMMIO_AO_2ND_DAC_OFFSET 0x04
-
-/*
- * WinSystems WS16C48
- *
- * Offset Page 0 Page 1 Page 2 Page 3
- * ------ ----------- ----------- ----------- -----------
- * 0x10 Port 0 I/O Port 0 I/O Port 0 I/O Port 0 I/O
- * 0x11 Port 1 I/O Port 1 I/O Port 1 I/O Port 1 I/O
- * 0x12 Port 2 I/O Port 2 I/O Port 2 I/O Port 2 I/O
- * 0x13 Port 3 I/O Port 3 I/O Port 3 I/O Port 3 I/O
- * 0x14 Port 4 I/O Port 4 I/O Port 4 I/O Port 4 I/O
- * 0x15 Port 5 I/O Port 5 I/O Port 5 I/O Port 5 I/O
- * 0x16 INT_PENDING INT_PENDING INT_PENDING INT_PENDING
- * 0x17 Page/Lock Page/Lock Page/Lock Page/Lock
- * 0x18 N/A POL_0 ENAB_0 INT_ID0
- * 0x19 N/A POL_1 ENAB_1 INT_ID1
- * 0x1a N/A POL_2 ENAB_2 INT_ID2
- */
-#define PCMMIO_PORT_REG(x) (0x10 + (x))
-#define PCMMIO_INT_PENDING_REG 0x16
-#define PCMMIO_PAGE_LOCK_REG 0x17
-#define PCMMIO_LOCK_PORT(x) ((1 << (x)) & 0x3f)
-#define PCMMIO_PAGE(x) (((x) & 0x3) << 6)
-#define PCMMIO_PAGE_MASK PCMUIO_PAGE(3)
-#define PCMMIO_PAGE_POL 1
-#define PCMMIO_PAGE_ENAB 2
-#define PCMMIO_PAGE_INT_ID 3
-#define PCMMIO_PAGE_REG(x) (0x18 + (x))
-
-static const struct comedi_lrange pcmmio_ai_ranges = {
- 4, {
- BIP_RANGE(5),
- BIP_RANGE(10),
- UNI_RANGE(5),
- UNI_RANGE(10)
- }
-};
-
-static const struct comedi_lrange pcmmio_ao_ranges = {
- 6, {
- UNI_RANGE(5),
- UNI_RANGE(10),
- BIP_RANGE(5),
- BIP_RANGE(10),
- BIP_RANGE(2.5),
- RANGE(-2.5, 7.5)
- }
-};
-
-struct pcmmio_private {
- spinlock_t pagelock; /* protects the page registers */
- spinlock_t spinlock; /* protects the member variables */
- unsigned int enabled_mask;
- unsigned int active:1;
-};
-
-static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
- int page, int port)
-{
- struct pcmmio_private *devpriv = dev->private;
- unsigned long iobase = dev->iobase;
- unsigned long flags;
-
- spin_lock_irqsave(&devpriv->pagelock, flags);
- if (page == 0) {
- /* Port registers are valid for any page */
- outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
- outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
- outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
- } else {
- outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
- outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
- outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
- outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
- }
- spin_unlock_irqrestore(&devpriv->pagelock, flags);
-}
-
-static unsigned int pcmmio_dio_read(struct comedi_device *dev,
- int page, int port)
-{
- struct pcmmio_private *devpriv = dev->private;
- unsigned long iobase = dev->iobase;
- unsigned long flags;
- unsigned int val;
-
- spin_lock_irqsave(&devpriv->pagelock, flags);
- if (page == 0) {
- /* Port registers are valid for any page */
- val = inb(iobase + PCMMIO_PORT_REG(port + 0));
- val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
- val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
- } else {
- outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
- val = inb(iobase + PCMMIO_PAGE_REG(0));
- val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
- val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
- }
- spin_unlock_irqrestore(&devpriv->pagelock, flags);
-
- return val;
-}
-
-/*
- * Each channel can be individually programmed for input or output.
- * Writing a '0' to a channel causes the corresponding output pin
- * to go to a high-z state (pulled high by an external 10K resistor).
- * This allows it to be used as an input. When used in the input mode,
- * a read reflects the inverted state of the I/O pin, such that a
- * high on the pin will read as a '0' in the register. Writing a '1'
- * to a bit position causes the pin to sink current (up to 12mA),
- * effectively pulling it low.
- */
-static int pcmmio_dio_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
- int port = s->index == 2 ? 0 : 3;
- unsigned int chanmask = (1 << s->n_chan) - 1;
- unsigned int mask;
- unsigned int val;
-
- mask = comedi_dio_update_state(s, data);
- if (mask) {
- /*
- * Outputs are inverted, invert the state and
- * update the channels.
- *
- * The s->io_bits mask makes sure the input channels
- * are '0' so that the outputs pins stay in a high
- * z-state.
- */
- val = ~s->state & chanmask;
- val &= s->io_bits;
- pcmmio_dio_write(dev, val, 0, port);
- }
-
- /* get inverted state of the channels from the port */
- val = pcmmio_dio_read(dev, 0, port);
-
- /* return the true state of the channels */
- data[1] = ~val & chanmask;
-
- return insn->n;
-}
-
-static int pcmmio_dio_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
- int port = s->index == 2 ? 0 : 3;
- int ret;
-
- ret = comedi_dio_insn_config(dev, s, insn, data, 0);
- if (ret)
- return ret;
-
- if (data[0] == INSN_CONFIG_DIO_INPUT)
- pcmmio_dio_write(dev, s->io_bits, 0, port);
-
- return insn->n;
-}
-
-static void pcmmio_reset(struct comedi_device *dev)
-{
- /* Clear all the DIO port bits */
- pcmmio_dio_write(dev, 0, 0, 0);
- pcmmio_dio_write(dev, 0, 0, 3);
-
- /* Clear all the paged registers */
- pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
- pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
- pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
-}
-
-/* devpriv->spinlock is already locked */
-static void pcmmio_stop_intr(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pcmmio_private *devpriv = dev->private;
-
- devpriv->enabled_mask = 0;
- devpriv->active = 0;
- s->async->inttrig = NULL;
-
- /* disable all dio interrupts */
- pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
-}
-
-static void pcmmio_handle_dio_intr(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int triggered)
-{
- struct pcmmio_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int val = 0;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&devpriv->spinlock, flags);
-
- if (!devpriv->active)
- goto done;
-
- if (!(triggered & devpriv->enabled_mask))
- goto done;
-
- for (i = 0; i < cmd->chanlist_len; i++) {
- unsigned int chan = CR_CHAN(cmd->chanlist[i]);
-
- if (triggered & (1 << chan))
- val |= (1 << i);
- }
-
- comedi_buf_write_samples(s, &val, 1);
-
- if (cmd->stop_src == TRIG_COUNT &&
- s->async->scans_done >= cmd->stop_arg)
- s->async->events |= COMEDI_CB_EOA;
-
-done:
- spin_unlock_irqrestore(&devpriv->spinlock, flags);
-
- comedi_handle_events(dev, s);
-}
-
-static irqreturn_t interrupt_pcmmio(int irq, void *d)
-{
- struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->read_subdev;
- unsigned int triggered;
- unsigned char int_pend;
-
- /* are there any interrupts pending */
- int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
- if (!int_pend)
- return IRQ_NONE;
-
- /* get, and clear, the pending interrupts */
- triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
- pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
-
- pcmmio_handle_dio_intr(dev, s, triggered);
-
- return IRQ_HANDLED;
-}
-
-/* devpriv->spinlock is already locked */
-static void pcmmio_start_intr(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pcmmio_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int bits = 0;
- unsigned int pol_bits = 0;
- int i;
-
- devpriv->enabled_mask = 0;
- devpriv->active = 1;
- if (cmd->chanlist) {
- for (i = 0; i < cmd->chanlist_len; i++) {
- unsigned int chanspec = cmd->chanlist[i];
- unsigned int chan = CR_CHAN(chanspec);
- unsigned int range = CR_RANGE(chanspec);
- unsigned int aref = CR_AREF(chanspec);
-
- bits |= (1 << chan);
- pol_bits |= (((aref || range) ? 1 : 0) << chan);
- }
- }
- bits &= ((1 << s->n_chan) - 1);
- devpriv->enabled_mask = bits;
-
- /* set polarity and enable interrupts */
- pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
- pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
-}
-
-static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- struct pcmmio_private *devpriv = dev->private;
- unsigned long flags;
-
- spin_lock_irqsave(&devpriv->spinlock, flags);
- if (devpriv->active)
- pcmmio_stop_intr(dev, s);
- spin_unlock_irqrestore(&devpriv->spinlock, flags);
-
- return 0;
-}
-
-static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int trig_num)
-{
- struct pcmmio_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned long flags;
-
- if (trig_num != cmd->start_arg)
- return -EINVAL;
-
- spin_lock_irqsave(&devpriv->spinlock, flags);
- s->async->inttrig = NULL;
- if (devpriv->active)
- pcmmio_start_intr(dev, s);
- spin_unlock_irqrestore(&devpriv->spinlock, flags);
-
- return 1;
-}
-
-/*
- * 'do_cmd' function for an 'INTERRUPT' subdevice.
- */
-static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- struct pcmmio_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned long flags;
-
- spin_lock_irqsave(&devpriv->spinlock, flags);
- devpriv->active = 1;
-
- /* Set up start of acquisition. */
- if (cmd->start_src == TRIG_INT)
- s->async->inttrig = pcmmio_inttrig_start_intr;
- else /* TRIG_NOW */
- pcmmio_start_intr(dev, s);
-
- spin_unlock_irqrestore(&devpriv->spinlock, flags);
-
- return 0;
-}
-
-static int pcmmio_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 | TRIG_INT);
- err |= comedi_check_trigger_src(&cmd->scan_begin_src, 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->start_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);
- err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
- 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: fix up any arguments */
-
- /* if (err) return 4; */
-
- return 0;
-}
-
-static int pcmmio_ai_eoc(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned long context)
-{
- unsigned char status;
-
- status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
- if (status & PCMMIO_AI_STATUS_DATA_READY)
- return 0;
- return -EBUSY;
-}
-
-static int pcmmio_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- unsigned long iobase = dev->iobase;
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int range = CR_RANGE(insn->chanspec);
- unsigned int aref = CR_AREF(insn->chanspec);
- unsigned char cmd = 0;
- unsigned int val;
- int ret;
- int i;
-
- /*
- * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters.
- * The devices use a full duplex serial interface which transmits and
- * receives data simultaneously. An 8-bit command is shifted into the
- * ADC interface to configure it for the next conversion. At the same
- * time, the data from the previous conversion is shifted out of the
- * device. Consequently, the conversion result is delayed by one
- * conversion from the command word.
- *
- * Setup the cmd for the conversions then do a dummy conversion to
- * flush the junk data. Then do each conversion requested by the
- * comedi_insn. Note that the last conversion will leave junk data
- * in ADC which will get flushed on the next comedi_insn.
- */
-
- if (chan > 7) {
- chan -= 8;
- iobase += PCMMIO_AI_2ND_ADC_OFFSET;
- }
-
- if (aref == AREF_GROUND)
- cmd |= PCMMIO_AI_CMD_SE;
- if (chan % 2)
- cmd |= PCMMIO_AI_CMD_ODD_CHAN;
- cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
- cmd |= PCMMIO_AI_CMD_RANGE(range);
-
- outb(cmd, iobase + PCMMIO_AI_CMD_REG);
-
- ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
- if (ret)
- return ret;
-
- val = inb(iobase + PCMMIO_AI_LSB_REG);
- val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
-
- for (i = 0; i < insn->n; i++) {
- outb(cmd, iobase + PCMMIO_AI_CMD_REG);
-
- ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
- if (ret)
- return ret;
-
- val = inb(iobase + PCMMIO_AI_LSB_REG);
- val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
-
- /* bipolar data is two's complement */
- if (comedi_range_is_bipolar(s, range))
- val = comedi_offset_munge(s, val);
-
- data[i] = val;
- }
-
- return insn->n;
-}
-
-static int pcmmio_ao_eoc(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned long context)
-{
- unsigned char status;
-
- status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
- if (status & PCMMIO_AO_STATUS_DATA_READY)
- return 0;
- return -EBUSY;
-}
-
-static int pcmmio_ao_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- unsigned long iobase = dev->iobase;
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int range = CR_RANGE(insn->chanspec);
- unsigned char cmd = 0;
- int ret;
- int i;
-
- /*
- * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
- * is a 4-channel converter with software-selectable output range.
- */
-
- if (chan > 3) {
- cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
- iobase += PCMMIO_AO_2ND_DAC_OFFSET;
- } else {
- cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
- }
-
- /* set the range for the channel */
- outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
- outb(0, iobase + PCMMIO_AO_MSB_REG);
- outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
-
- ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
- if (ret)
- return ret;
-
- for (i = 0; i < insn->n; i++) {
- unsigned int val = data[i];
-
- /* write the data to the channel */
- outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
- outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
- outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
- iobase + PCMMIO_AO_CMD_REG);
-
- ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
- if (ret)
- return ret;
-
- s->readback[chan] = val;
- }
-
- return insn->n;
-}
-
-static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
-{
- struct pcmmio_private *devpriv;
- struct comedi_subdevice *s;
- int ret;
-
- ret = comedi_request_region(dev, it->options[0], 32);
- if (ret)
- return ret;
-
- devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
- if (!devpriv)
- return -ENOMEM;
-
- spin_lock_init(&devpriv->pagelock);
- spin_lock_init(&devpriv->spinlock);
-
- pcmmio_reset(dev);
-
- if (it->options[1]) {
- ret = request_irq(it->options[1], interrupt_pcmmio, 0,
- dev->board_name, dev);
- if (ret == 0) {
- dev->irq = it->options[1];
-
- /* configure the interrupt routing on the board */
- outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
- dev->iobase + PCMMIO_AI_RES_ENA_REG);
- outb(PCMMIO_RESOURCE_IRQ(dev->irq),
- dev->iobase + PCMMIO_RESOURCE_REG);
- }
- }
-
- 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 | SDF_DIFF;
- s->n_chan = 16;
- s->maxdata = 0xffff;
- s->range_table = &pcmmio_ai_ranges;
- s->insn_read = pcmmio_ai_insn_read;
-
- /* initialize the resource enable register by clearing it */
- outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
- dev->iobase + PCMMIO_AI_RES_ENA_REG);
- outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
- dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
-
- /* Analog Output subdevice */
- s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 8;
- s->maxdata = 0xffff;
- s->range_table = &pcmmio_ao_ranges;
- s->insn_write = pcmmio_ao_insn_write;
-
- ret = comedi_alloc_subdev_readback(s);
- if (ret)
- return ret;
-
- /* initialize the resource enable register by clearing it */
- outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
- outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
- PCMMIO_AO_RESOURCE_ENA_REG);
-
- /* Digital I/O subdevice with interrupt support */
- s = &dev->subdevices[2];
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 24;
- s->maxdata = 1;
- s->len_chanlist = 1;
- s->range_table = &range_digital;
- s->insn_bits = pcmmio_dio_insn_bits;
- s->insn_config = pcmmio_dio_insn_config;
- if (dev->irq) {
- dev->read_subdev = s;
- s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL | SDF_PACKED;
- s->len_chanlist = s->n_chan;
- s->cancel = pcmmio_cancel;
- s->do_cmd = pcmmio_cmd;
- s->do_cmdtest = pcmmio_cmdtest;
- }
-
- /* Digital I/O subdevice */
- s = &dev->subdevices[3];
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 24;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = pcmmio_dio_insn_bits;
- s->insn_config = pcmmio_dio_insn_config;
-
- return 0;
-}
-
-static struct comedi_driver pcmmio_driver = {
- .driver_name = "pcmmio",
- .module = THIS_MODULE,
- .attach = pcmmio_attach,
- .detach = comedi_legacy_detach,
-};
-module_comedi_driver(pcmmio_driver);
-
-MODULE_AUTHOR("Comedi https://www.comedi.org");
-MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
-MODULE_LICENSE("GPL");