linux/drivers/comedi/drivers/das1800.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Comedi driver for Keithley DAS-1700/DAS-1800 series boards
 * Copyright (C) 2000 Frank Mori Hess <[email protected]>
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 2000 David A. Schleef <[email protected]>
 */

/*
 * Driver: das1800
 * Description: Keithley Metrabyte DAS1800 (& compatibles)
 * Author: Frank Mori Hess <[email protected]>
 * Devices: [Keithley Metrabyte] DAS-1701ST (das-1701st),
 *   DAS-1701ST-DA (das-1701st-da), DAS-1701/AO (das-1701ao),
 *   DAS-1702ST (das-1702st), DAS-1702ST-DA (das-1702st-da),
 *   DAS-1702HR (das-1702hr), DAS-1702HR-DA (das-1702hr-da),
 *   DAS-1702/AO (das-1702ao), DAS-1801ST (das-1801st),
 *   DAS-1801ST-DA (das-1801st-da), DAS-1801HC (das-1801hc),
 *   DAS-1801AO (das-1801ao), DAS-1802ST (das-1802st),
 *   DAS-1802ST-DA (das-1802st-da), DAS-1802HR (das-1802hr),
 *   DAS-1802HR-DA (das-1802hr-da), DAS-1802HC (das-1802hc),
 *   DAS-1802AO (das-1802ao)
 * Status: works
 *
 * Configuration options:
 *   [0] - I/O port base address
 *   [1] - IRQ (optional, required for analog input cmd support)
 *   [2] - DMA0 (optional, requires irq)
 *   [3] - DMA1 (optional, requires irq and dma0)
 *
 * analog input cmd triggers supported:
 *
 *   start_src		TRIG_NOW	command starts immediately
 *			TRIG_EXT	command starts on external pin TGIN
 *
 *   scan_begin_src	TRIG_FOLLOW	paced/external scans start immediately
 *			TRIG_TIMER	burst scans start periodically
 *			TRIG_EXT	burst scans start on external pin XPCLK
 *
 *   scan_end_src	TRIG_COUNT	scan ends after last channel
 *
 *   convert_src	TRIG_TIMER	paced/burst conversions are timed
 *			TRIG_EXT	conversions on external pin XPCLK
 *					(requires scan_begin_src == TRIG_FOLLOW)
 *
 *   stop_src		TRIG_COUNT	command stops after stop_arg scans
 *			TRIG_EXT	command stops on external pin TGIN
 *			TRIG_NONE	command runs until canceled
 *
 * If TRIG_EXT is used for both the start_src and stop_src, the first TGIN
 * trigger starts the command, and the second trigger will stop it. If only
 * one is TRIG_EXT, the first trigger will either stop or start the command.
 * The external pin TGIN is normally set for negative edge triggering. It
 * can be set to positive edge with the CR_INVERT flag. If TRIG_EXT is used
 * for both the start_src and stop_src they must have the same polarity.
 *
 * Minimum conversion speed is limited to 64 microseconds (convert_arg <= 64000)
 * for 'burst' scans. This limitation does not apply for 'paced' scans. The
 * maximum conversion speed is limited by the board (convert_arg >= ai_speed).
 * Maximum conversion speeds are not always achievable depending on the
 * board setup (see user manual).
 *
 * NOTES:
 * Only the DAS-1801ST has been tested by me.
 * Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
 *
 * The waveform analog output on the 'ao' cards is not supported.
 * If you need it, send me (Frank Hess) an email.
 */

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/comedi/comedidev.h>
#include <linux/comedi/comedi_8254.h>
#include <linux/comedi/comedi_isadma.h>

/* misc. defines */
#define DAS1800_SIZE
#define FIFO_SIZE
#define DMA_BUF_SIZE

/* Registers for the das1800 */
#define DAS1800_FIFO
#define DAS1800_QRAM
#define DAS1800_DAC
#define DAS1800_SELECT
#define ADC
#define QRAM
#define DAC(a)
#define DAS1800_DIGITAL
#define DAS1800_CONTROL_A
#define FFEN
#define CGEN
#define CGSL
#define TGEN
#define TGSL
#define TGPL
#define ATEN
#define DAS1800_CONTROL_B
#define DMA_CH5
#define DMA_CH6
#define DMA_CH7
#define DMA_CH5_CH6
#define DMA_CH6_CH7
#define DMA_CH7_CH5
#define DMA_ENABLED
#define DMA_DUAL
#define IRQ3
#define IRQ5
#define IRQ7
#define IRQ10
#define IRQ11
#define IRQ15
#define FIMD
#define DAS1800_CONTROL_C
#define IPCLK
#define XPCLK
#define BMDE
#define CMEN
#define UQEN
#define SD
#define UB
#define DAS1800_STATUS
#define INT
#define DMATC
#define CT0TC
#define OVF
#define FHF
#define FNE
#define CVEN
#define CVEN_MASK
#define CLEAR_INTR_MASK
#define DAS1800_BURST_LENGTH
#define DAS1800_BURST_RATE
#define DAS1800_QRAM_ADDRESS
#define DAS1800_COUNTER

#define IOBASE2

static const struct comedi_lrange das1801_ai_range =;

static const struct comedi_lrange das1802_ai_range =;

/*
 * The waveform analog outputs on the 'ao' boards are not currently
 * supported. They have a comedi_lrange of:
 * { 2, { BIP_RANGE(10), BIP_RANGE(5) } }
 */

enum das1800_boardid {};

/* board probe id values (hi byte of the digital input register) */
#define DAS1800_ID_ST_DA
#define DAS1800_ID_HR_DA
#define DAS1800_ID_AO
#define DAS1800_ID_HR
#define DAS1800_ID_ST
#define DAS1800_ID_HC

struct das1800_board {};

static const struct das1800_board das1800_boards[] =;

struct das1800_private {};

static void das1800_ai_munge(struct comedi_device *dev,
			     struct comedi_subdevice *s,
			     void *data, unsigned int num_bytes,
			     unsigned int start_chan_index)
{}

static void das1800_handle_fifo_half_full(struct comedi_device *dev,
					  struct comedi_subdevice *s)
{}

static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
					  struct comedi_subdevice *s)
{}

static void das1800_flush_dma_channel(struct comedi_device *dev,
				      struct comedi_subdevice *s,
				      struct comedi_isadma_desc *desc)
{}

static void das1800_flush_dma(struct comedi_device *dev,
			      struct comedi_subdevice *s)
{}

static void das1800_handle_dma(struct comedi_device *dev,
			       struct comedi_subdevice *s, unsigned int status)
{}

static int das1800_ai_cancel(struct comedi_device *dev,
			     struct comedi_subdevice *s)
{}

static void das1800_ai_handler(struct comedi_device *dev)
{}

static int das1800_ai_poll(struct comedi_device *dev,
			   struct comedi_subdevice *s)
{}

static irqreturn_t das1800_interrupt(int irq, void *d)
{}

static int das1800_ai_fixup_paced_timing(struct comedi_device *dev,
					 struct comedi_cmd *cmd)
{}

static int das1800_ai_fixup_burst_timing(struct comedi_device *dev,
					 struct comedi_cmd *cmd)
{}

static int das1800_ai_check_chanlist(struct comedi_device *dev,
				     struct comedi_subdevice *s,
				     struct comedi_cmd *cmd)
{}

static int das1800_ai_cmdtest(struct comedi_device *dev,
			      struct comedi_subdevice *s,
			      struct comedi_cmd *cmd)
{}

static unsigned char das1800_ai_chanspec_bits(struct comedi_subdevice *s,
					      unsigned int chanspec)
{}

static unsigned int das1800_ai_transfer_size(struct comedi_device *dev,
					     struct comedi_subdevice *s,
					     unsigned int maxbytes,
					     unsigned int ns)
{}

static void das1800_ai_setup_dma(struct comedi_device *dev,
				 struct comedi_subdevice *s)
{}

static void das1800_ai_set_chanlist(struct comedi_device *dev,
				    unsigned int *chanlist, unsigned int len)
{}

static int das1800_ai_cmd(struct comedi_device *dev,
			  struct comedi_subdevice *s)
{}

static int das1800_ai_eoc(struct comedi_device *dev,
			  struct comedi_subdevice *s,
			  struct comedi_insn *insn,
			  unsigned long context)
{}

static int das1800_ai_insn_read(struct comedi_device *dev,
				struct comedi_subdevice *s,
				struct comedi_insn *insn,
				unsigned int *data)
{}

static int das1800_ao_insn_write(struct comedi_device *dev,
				 struct comedi_subdevice *s,
				 struct comedi_insn *insn,
				 unsigned int *data)
{}

static int das1800_di_insn_bits(struct comedi_device *dev,
				struct comedi_subdevice *s,
				struct comedi_insn *insn,
				unsigned int *data)
{}

static int das1800_do_insn_bits(struct comedi_device *dev,
				struct comedi_subdevice *s,
				struct comedi_insn *insn,
				unsigned int *data)
{}

static void das1800_init_dma(struct comedi_device *dev,
			     struct comedi_devconfig *it)
{}

static void das1800_free_dma(struct comedi_device *dev)
{}

static int das1800_probe(struct comedi_device *dev)
{}

static int das1800_attach(struct comedi_device *dev,
			  struct comedi_devconfig *it)
{
	const struct das1800_board *board;
	struct das1800_private *devpriv;
	struct comedi_subdevice *s;
	unsigned int irq = it->options[1];
	bool is_16bit;
	int ret;
	int i;

	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
	if (!devpriv)
		return -ENOMEM;

	ret = comedi_request_region(dev, it->options[0], DAS1800_SIZE);
	if (ret)
		return ret;

	ret = das1800_probe(dev);
	if (ret)
		return ret;
	board = dev->board_ptr;

	is_16bit = board->id == DAS1800_ID_HR || board->id == DAS1800_ID_HR_DA;

	/* waveform 'ao' boards have additional io ports */
	if (board->id == DAS1800_ID_AO) {
		unsigned long iobase2 = dev->iobase + IOBASE2;

		ret = __comedi_request_region(dev, iobase2, DAS1800_SIZE);
		if (ret)
			return ret;
		devpriv->iobase2 = iobase2;
	}

	if (irq == 3 || irq == 5 || irq == 7 || irq == 10 || irq == 11 ||
	    irq == 15) {
		ret = request_irq(irq, das1800_interrupt, 0,
				  dev->board_name, dev);
		if (ret == 0) {
			dev->irq = irq;

			switch (irq) {
			case 3:
				devpriv->irq_dma_bits |= 0x8;
				break;
			case 5:
				devpriv->irq_dma_bits |= 0x10;
				break;
			case 7:
				devpriv->irq_dma_bits |= 0x18;
				break;
			case 10:
				devpriv->irq_dma_bits |= 0x28;
				break;
			case 11:
				devpriv->irq_dma_bits |= 0x30;
				break;
			case 15:
				devpriv->irq_dma_bits |= 0x38;
				break;
			}
		}
	}

	/* an irq and one dma channel is required to use dma */
	if (dev->irq & it->options[2])
		das1800_init_dma(dev, it);

	devpriv->fifo_buf = kmalloc_array(FIFO_SIZE,
					  sizeof(*devpriv->fifo_buf),
					  GFP_KERNEL);
	if (!devpriv->fifo_buf)
		return -ENOMEM;

	dev->pacer = comedi_8254_io_alloc(dev->iobase + DAS1800_COUNTER,
					  I8254_OSC_BASE_5MHZ, I8254_IO8, 0);
	if (IS_ERR(dev->pacer))
		return PTR_ERR(dev->pacer);

	ret = comedi_alloc_subdevices(dev, 4);
	if (ret)
		return ret;

	/*
	 * Analog Input subdevice
	 *
	 * The "hc" type boards have 64 analog input channels and a 64
	 * entry QRAM fifo.
	 *
	 * All the other board types have 16 on-board channels. Each channel
	 * can be expanded to 16 channels with the addition of an EXP-1800
	 * expansion board for a total of 256 channels. The QRAM fifo on
	 * these boards has 256 entries.
	 *
	 * From the datasheets it's not clear what the comedi channel to
	 * actual physical channel mapping is when EXP-1800 boards are used.
	 */
	s = &dev->subdevices[0];
	s->type		= COMEDI_SUBD_AI;
	s->subdev_flags	= SDF_READABLE | SDF_DIFF | SDF_GROUND;
	if (board->id != DAS1800_ID_HC)
		s->subdev_flags	|= SDF_COMMON;
	s->n_chan	= (board->id == DAS1800_ID_HC) ? 64 : 256;
	s->maxdata	= is_16bit ? 0xffff : 0x0fff;
	s->range_table	= board->is_01_series ? &das1801_ai_range
					      : &das1802_ai_range;
	s->insn_read	= das1800_ai_insn_read;
	if (dev->irq) {
		dev->read_subdev = s;
		s->subdev_flags	|= SDF_CMD_READ;
		s->len_chanlist	= s->n_chan;
		s->do_cmd	= das1800_ai_cmd;
		s->do_cmdtest	= das1800_ai_cmdtest;
		s->poll		= das1800_ai_poll;
		s->cancel	= das1800_ai_cancel;
		s->munge	= das1800_ai_munge;
	}

	/* Analog Output subdevice */
	s = &dev->subdevices[1];
	if (board->id == DAS1800_ID_ST_DA || board->id == DAS1800_ID_HR_DA) {
		s->type		= COMEDI_SUBD_AO;
		s->subdev_flags	= SDF_WRITABLE;
		s->n_chan	= (board->id == DAS1800_ID_ST_DA) ? 4 : 2;
		s->maxdata	= is_16bit ? 0xffff : 0x0fff;
		s->range_table	= &range_bipolar10;
		s->insn_write	= das1800_ao_insn_write;

		ret = comedi_alloc_subdev_readback(s);
		if (ret)
			return ret;

		/* initialize all channels to 0V */
		for (i = 0; i < s->n_chan; i++) {
			/* spinlock is not necessary during the attach */
			outb(DAC(i), dev->iobase + DAS1800_SELECT);
			outw(0, dev->iobase + DAS1800_DAC);
		}
	} else if (board->id == DAS1800_ID_AO) {
		/*
		 * 'ao' boards have waveform analog outputs that are not
		 * currently supported.
		 */
		s->type		= COMEDI_SUBD_UNUSED;
	} else {
		s->type		= COMEDI_SUBD_UNUSED;
	}

	/* Digital Input subdevice */
	s = &dev->subdevices[2];
	s->type		= COMEDI_SUBD_DI;
	s->subdev_flags	= SDF_READABLE;
	s->n_chan	= 4;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_bits	= das1800_di_insn_bits;

	/* Digital Output subdevice */
	s = &dev->subdevices[3];
	s->type		= COMEDI_SUBD_DO;
	s->subdev_flags	= SDF_WRITABLE;
	s->n_chan	= (board->id == DAS1800_ID_HC) ? 8 : 4;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_bits	= das1800_do_insn_bits;

	das1800_ai_cancel(dev, dev->read_subdev);

	/*  initialize digital out channels */
	outb(0, dev->iobase + DAS1800_DIGITAL);

	return 0;
};

static void das1800_detach(struct comedi_device *dev)
{}

static struct comedi_driver das1800_driver =;
module_comedi_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();