linux/drivers/comedi/drivers/cb_pcidas64.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * comedi/drivers/cb_pcidas64.c
 * This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
 * 64xx, 60xx, and 4020 cards.
 *
 * Author:  Frank Mori Hess <[email protected]>
 * Copyright (C) 2001, 2002 Frank Mori Hess
 *
 * Thanks also go to the following people:
 *
 * Steve Rosenbluth, for providing the source code for
 * his pci-das6402 driver, and source code for working QNX pci-6402
 * drivers by Greg Laird and Mariusz Bogacz.  None of the code was
 * used directly here, but it was useful as an additional source of
 * documentation on how to program the boards.
 *
 * John Sims, for much testing and feedback on pcidas-4020 support.
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 1997-8 David A. Schleef <[email protected]>
 */

/*
 * Driver: cb_pcidas64
 * Description: MeasurementComputing PCI-DAS64xx, 60XX, and 4020 series
 *   with the PLX 9080 PCI controller
 * Author: Frank Mori Hess <[email protected]>
 * Status: works
 * Updated: Fri, 02 Nov 2012 18:58:55 +0000
 * Devices: [Measurement Computing] PCI-DAS6402/16 (cb_pcidas64),
 *   PCI-DAS6402/12, PCI-DAS64/M1/16, PCI-DAS64/M2/16,
 *   PCI-DAS64/M3/16, PCI-DAS6402/16/JR, PCI-DAS64/M1/16/JR,
 *   PCI-DAS64/M2/16/JR, PCI-DAS64/M3/16/JR, PCI-DAS64/M1/14,
 *   PCI-DAS64/M2/14, PCI-DAS64/M3/14, PCI-DAS6013, PCI-DAS6014,
 *   PCI-DAS6023, PCI-DAS6025, PCI-DAS6030,
 *   PCI-DAS6031, PCI-DAS6032, PCI-DAS6033, PCI-DAS6034,
 *   PCI-DAS6035, PCI-DAS6036, PCI-DAS6040, PCI-DAS6052,
 *   PCI-DAS6070, PCI-DAS6071, PCI-DAS4020/12
 *
 * Configuration options:
 *   None.
 *
 * Manual attachment of PCI cards with the comedi_config utility is not
 * supported by this driver; they are attached automatically.
 *
 * These boards may be autocalibrated with the comedi_calibrate utility.
 *
 * To select the bnc trigger input on the 4020 (instead of the dio input),
 * specify a nonzero channel in the chanspec.  If you wish to use an external
 * master clock on the 4020, you may do so by setting the scan_begin_src
 * to TRIG_OTHER, and using an INSN_CONFIG_TIMER_1 configuration insn
 * to configure the divisor to use for the external clock.
 *
 * Some devices are not identified because the PCI device IDs are not yet
 * known. If you have such a board, please let the maintainers know.
 */

/*
 * TODO:
 * make it return error if user attempts an ai command that uses the
 * external queue, and an ao command simultaneously user counter subdevice
 * there are a number of boards this driver will support when they are
 * fully released, but does not yet since the pci device id numbers
 * are not yet available.
 *
 * support prescaled 100khz clock for slow pacing (not available on 6000
 * series?)
 *
 * make ao fifo size adjustable like ai fifo
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/comedi/comedi_pci.h>
#include <linux/comedi/comedi_8255.h>

#include "plx9080.h"

#define TIMER_BASE
/*
 * 100kHz 'prescaled' clock for slow acquisition,
 * maybe I'll support this someday
 */
#define PRESCALED_TIMER_BASE
#define DMA_BUFFER_SIZE
#define DAC_FIFO_SIZE

/* maximum value that can be loaded into board's 24-bit counters */
static const int max_counter_value =;

/* PCI-DAS64xxx base addresses */

/* devpriv->main_iobase registers */
enum write_only_registers {};

static inline unsigned int dac_convert_reg(unsigned int channel)
{}

static inline unsigned int dac_lsb_4020_reg(unsigned int channel)
{}

static inline unsigned int dac_msb_4020_reg(unsigned int channel)
{}

enum read_only_registers {};

enum read_write_registers {};

/* dev->mmio registers */
enum dio_counter_registers {};

/* bit definitions for write-only registers */

enum intr_enable_contents {};

enum hw_config_contents {};

enum daq_atrig_low_4020_contents {};

enum adc_control0_contents {};

enum adc_control1_contents {};

static inline u16 adc_lo_chan_4020_bits(unsigned int channel)
{
	return (channel & 0x3) << 8;
};

static inline u16 adc_hi_chan_4020_bits(unsigned int channel)
{
	return (channel & 0x3) << 10;
};

static inline u16 adc_mode_bits(unsigned int mode)
{
	return (mode & 0xf) << 12;
};

enum calibration_contents {};

/*
 * calibration sources for 6025 are:
 *  0 : ground
 *  1 : 10V
 *  2 : 5V
 *  3 : 0.5V
 *  4 : 0.05V
 *  5 : ground
 *  6 : dac channel 0
 *  7 : dac channel 1
 */

static inline u16 adc_src_bits(unsigned int source)
{
	return (source & 0xf) << 3;
};

static inline u16 adc_convert_chan_4020_bits(unsigned int channel)
{
	return (channel & 0x3) << 8;
};

enum adc_queue_load_contents {};

static inline u16 adc_chan_bits(unsigned int channel)
{
	return channel & 0x3f;
};

enum dac_control0_contents {};

enum dac_control1_contents {};

/* bit definitions for read-only registers */
enum hw_status_contents {};

static inline u16 pipe_full_bits(u16 hw_status_bits)
{
	return (hw_status_bits >> 10) & 0x3;
};

static inline unsigned int adc_upper_read_ptr_code(u16 prepost_bits)
{}

static inline unsigned int adc_upper_write_ptr_code(u16 prepost_bits)
{}

/* I2C addresses for 4020 */
enum i2c_addresses {};

enum range_cal_i2c_contents {};

static inline u8 adc_src_4020_bits(unsigned int source)
{
	return (source << 4) & ADC_SRC_4020_MASK;
};

static inline u8 attenuate_bit(unsigned int channel)
{
	/* attenuate channel (+-5V input range) */
	return 1 << (channel & 0x3);
};

/* analog input ranges for 64xx boards */
static const struct comedi_lrange ai_ranges_64xx =;

static const u8 ai_range_code_64xx[8] =;

/* analog input ranges for 64-Mx boards */
static const struct comedi_lrange ai_ranges_64_mx =;

static const u8 ai_range_code_64_mx[7] =;

/* analog input ranges for 60xx boards */
static const struct comedi_lrange ai_ranges_60xx =;

static const u8 ai_range_code_60xx[4] =;

/* analog input ranges for 6030, etc boards */
static const struct comedi_lrange ai_ranges_6030 =;

static const u8 ai_range_code_6030[14] =;

/* analog input ranges for 6052, etc boards */
static const struct comedi_lrange ai_ranges_6052 =;

static const u8 ai_range_code_6052[15] =;

/* analog input ranges for 4020 board */
static const struct comedi_lrange ai_ranges_4020 =;

/* analog output ranges */
static const struct comedi_lrange ao_ranges_64xx =;

static const int ao_range_code_64xx[] =;

static const int ao_range_code_60xx[] =;

static const struct comedi_lrange ao_ranges_6030 =;

static const int ao_range_code_6030[] =;

static const struct comedi_lrange ao_ranges_4020 =;

static const int ao_range_code_4020[] =;

enum register_layout {};

struct hw_fifo_info {};

enum pcidas64_boardid {};

struct pcidas64_board {};

static const struct hw_fifo_info ai_fifo_4020 =;

static const struct hw_fifo_info ai_fifo_64xx =;

static const struct hw_fifo_info ai_fifo_60xx =;

/*
 * maximum number of dma transfers we will chain together into a ring
 * (and the maximum number of dma buffers we maintain)
 */
#define MAX_AI_DMA_RING_COUNT
#define MIN_AI_DMA_RING_COUNT
#define AO_DMA_RING_COUNT
static inline unsigned int ai_dma_ring_count(const struct pcidas64_board *board)
{}

static const int bytes_in_sample =;

static const struct pcidas64_board pcidas64_boards[] =;

static inline unsigned short se_diff_bit_6xxx(struct comedi_device *dev,
					      int use_differential)
{}

struct ext_clock_info {};

/* this structure is for data unique to this hardware driver. */
struct pcidas64_private {};

static unsigned int ai_range_bits_6xxx(const struct comedi_device *dev,
				       unsigned int range_index)
{}

static unsigned int hw_revision(const struct comedi_device *dev,
				u16 hw_status_bits)
{}

static void set_dac_range_bits(struct comedi_device *dev,
			       u16 *bits, unsigned int channel,
			       unsigned int range)
{
	const struct pcidas64_board *board = dev->board_ptr;
	unsigned int code = board->ao_range_code[range];

	if (channel > 1)
		dev_err(dev->class_dev, "bug! bad channel?\n");
	if (code & ~0x3)
		dev_err(dev->class_dev, "bug! bad range code?\n");

	*bits &= ~(0x3 << (2 * channel));
	*bits |= code << (2 * channel);
};

static inline int ao_cmd_is_supported(const struct pcidas64_board *board)
{}

static void abort_dma(struct comedi_device *dev, unsigned int channel)
{}

static void disable_plx_interrupts(struct comedi_device *dev)
{}

static void disable_ai_interrupts(struct comedi_device *dev)
{}

static void enable_ai_interrupts(struct comedi_device *dev,
				 const struct comedi_cmd *cmd)
{}

/* initialize plx9080 chip */
static void init_plx9080(struct comedi_device *dev)
{}

static void disable_ai_pacing(struct comedi_device *dev)
{}

static int set_ai_fifo_segment_length(struct comedi_device *dev,
				      unsigned int num_entries)
{}

/*
 * adjusts the size of hardware fifo (which determines block size for dma xfers)
 */
static int set_ai_fifo_size(struct comedi_device *dev, unsigned int num_samples)
{}

/* query length of fifo */
static unsigned int ai_fifo_size(struct comedi_device *dev)
{}

static void init_stc_registers(struct comedi_device *dev)
{
	const struct pcidas64_board *board = dev->board_ptr;
	struct pcidas64_private *devpriv = dev->private;
	u16 bits;
	unsigned long flags;

	spin_lock_irqsave(&dev->spinlock, flags);

	/*
	 * bit should be set for 6025,
	 * although docs say boards with <= 16 chans should be cleared XXX
	 */
	if (1)
		devpriv->adc_control1_bits |= ADC_QUEUE_CONFIG_BIT;
	writew(devpriv->adc_control1_bits,
	       devpriv->main_iobase + ADC_CONTROL1_REG);

	/* 6402/16 manual says this register must be initialized to 0xff? */
	writew(0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);

	bits = SLOW_DAC_BIT | DMA_CH_SELECT_BIT;
	if (board->layout == LAYOUT_4020)
		bits |= INTERNAL_CLOCK_4020_BITS;
	devpriv->hw_config_bits |= bits;
	writew(devpriv->hw_config_bits,
	       devpriv->main_iobase + HW_CONFIG_REG);

	writew(0, devpriv->main_iobase + DAQ_SYNC_REG);
	writew(0, devpriv->main_iobase + CALIBRATION_REG);

	spin_unlock_irqrestore(&dev->spinlock, flags);

	/* set fifos to maximum size */
	devpriv->fifo_size_bits |= DAC_FIFO_BITS;
	set_ai_fifo_segment_length(dev, board->ai_fifo->max_segment_length);

	devpriv->dac_control1_bits = DAC_OUTPUT_ENABLE_BIT;
	devpriv->intr_enable_bits =
		/* EN_DAC_INTR_SRC_BIT | DAC_INTR_QEMPTY_BITS | */
		EN_DAC_DONE_INTR_BIT | EN_DAC_UNDERRUN_BIT;
	writew(devpriv->intr_enable_bits,
	       devpriv->main_iobase + INTR_ENABLE_REG);

	disable_ai_pacing(dev);
};

static int alloc_and_init_dma_members(struct comedi_device *dev)
{}

static void cb_pcidas64_free_dma(struct comedi_device *dev)
{}

static inline void warn_external_queue(struct comedi_device *dev)
{}

/*
 * their i2c requires a huge delay on setting clock or data high for some reason
 */
static const int i2c_high_udelay =;
static const int i2c_low_udelay =;

/* set i2c data line high or low */
static void i2c_set_sda(struct comedi_device *dev, int state)
{}

/* set i2c clock line high or low */
static void i2c_set_scl(struct comedi_device *dev, int state)
{}

static void i2c_write_byte(struct comedi_device *dev, u8 byte)
{}

/* we can't really read the lines, so fake it */
static int i2c_read_ack(struct comedi_device *dev)
{}

/* send start bit */
static void i2c_start(struct comedi_device *dev)
{}

/* send stop bit */
static void i2c_stop(struct comedi_device *dev)
{}

static void i2c_write(struct comedi_device *dev, unsigned int address,
		      const u8 *data, unsigned int length)
{}

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

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

static int ai_config_calibration_source(struct comedi_device *dev,
					unsigned int *data)
{}

static int ai_config_block_size(struct comedi_device *dev, unsigned int *data)
{}

static int ai_config_master_clock_4020(struct comedi_device *dev,
				       unsigned int *data)
{}

/* XXX could add support for 60xx series */
static int ai_config_master_clock(struct comedi_device *dev, unsigned int *data)
{}

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

/*
 * Gets nearest achievable timing given master clock speed, does not
 * take into account possible minimum/maximum divisor values.  Used
 * by other timing checking functions.
 */
static unsigned int get_divisor(unsigned int ns, unsigned int flags)
{}

/*
 * utility function that rounds desired timing to an achievable time, and
 * sets cmd members appropriately.
 * adc paces conversions from master clock by dividing by (x + 3) where x is
 * 24 bit number
 */
static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
{}

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

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

static int use_hw_sample_counter(struct comedi_cmd *cmd)
{}

static void setup_sample_counters(struct comedi_device *dev,
				  struct comedi_cmd *cmd)
{}

static inline unsigned int dma_transfer_size(struct comedi_device *dev)
{}

static u32 ai_convert_counter_6xxx(const struct comedi_device *dev,
				   const struct comedi_cmd *cmd)
{}

static u32 ai_scan_counter_6xxx(struct comedi_device *dev,
				struct comedi_cmd *cmd)
{}

static u32 ai_convert_counter_4020(struct comedi_device *dev,
				   struct comedi_cmd *cmd)
{}

static void select_master_clock_4020(struct comedi_device *dev,
				     const struct comedi_cmd *cmd)
{}

static void select_master_clock(struct comedi_device *dev,
				const struct comedi_cmd *cmd)
{}

static inline void dma_start_sync(struct comedi_device *dev,
				  unsigned int channel)
{}

static void set_ai_pacing(struct comedi_device *dev, struct comedi_cmd *cmd)
{}

static int use_internal_queue_6xxx(const struct comedi_cmd *cmd)
{}

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

static inline void load_first_dma_descriptor(struct comedi_device *dev,
					     unsigned int dma_channel,
					     unsigned int descriptor_bits)
{}

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

/* read num_samples from 16 bit wide ai fifo */
static void pio_drain_ai_fifo_16(struct comedi_device *dev)
{}

/*
 * Read from 32 bit wide ai fifo of 4020 - deal with insane grey coding of
 * pointers.  The pci-4020 hardware only supports dma transfers (it only
 * supports the use of pio for draining the last remaining points from the
 * fifo when a data acquisition operation has completed).
 */
static void pio_drain_ai_fifo_32(struct comedi_device *dev)
{}

/* empty fifo */
static void pio_drain_ai_fifo(struct comedi_device *dev)
{}

static void drain_dma_buffers(struct comedi_device *dev, unsigned int channel)
{}

static void handle_ai_interrupt(struct comedi_device *dev,
				unsigned short status,
				unsigned int plx_status)
{}

static inline unsigned int prev_ao_dma_index(struct comedi_device *dev)
{}

static int last_ao_dma_load_completed(struct comedi_device *dev)
{}

static inline int ao_dma_needs_restart(struct comedi_device *dev,
				       unsigned short dma_status)
{}

static void restart_ao_dma(struct comedi_device *dev)
{}

static unsigned int cb_pcidas64_ao_fill_buffer(struct comedi_device *dev,
					       struct comedi_subdevice *s,
					       unsigned short *dest,
					       unsigned int max_bytes)
{}

static unsigned int load_ao_dma_buffer(struct comedi_device *dev,
				       const struct comedi_cmd *cmd)
{}

static void load_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd)
{}

static void handle_ao_interrupt(struct comedi_device *dev,
				unsigned short status, unsigned int plx_status)
{}

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

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

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

static void set_dac_control0_reg(struct comedi_device *dev,
				 const struct comedi_cmd *cmd)
{}

static void set_dac_control1_reg(struct comedi_device *dev,
				 const struct comedi_cmd *cmd)
{}

static void set_dac_select_reg(struct comedi_device *dev,
			       const struct comedi_cmd *cmd)
{}

static unsigned int get_ao_divisor(unsigned int ns, unsigned int flags)
{}

static void set_dac_interval_regs(struct comedi_device *dev,
				  const struct comedi_cmd *cmd)
{}

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

static inline int external_ai_queue_in_use(struct comedi_device *dev)
{}

static int ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
		      unsigned int trig_num)
{}

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

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

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

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

static int dio_callback_4020(struct comedi_device *dev,
			     int dir, int port, int data, unsigned long iobase)
{}

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

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

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

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

/*
 * pci-6025 8800 caldac:
 * address 0 == dac channel 0 offset
 * address 1 == dac channel 0 gain
 * address 2 == dac channel 1 offset
 * address 3 == dac channel 1 gain
 * address 4 == fine adc offset
 * address 5 == coarse adc offset
 * address 6 == coarse adc gain
 * address 7 == fine adc gain
 */
/*
 * pci-6402/16 uses all 8 channels for dac:
 * address 0 == dac channel 0 fine gain
 * address 1 == dac channel 0 coarse gain
 * address 2 == dac channel 0 coarse offset
 * address 3 == dac channel 1 coarse offset
 * address 4 == dac channel 1 fine gain
 * address 5 == dac channel 1 coarse gain
 * address 6 == dac channel 0 fine offset
 * address 7 == dac channel 1 fine offset
 */

static int caldac_8800_write(struct comedi_device *dev, unsigned int address,
			     u8 value)
{}

/* 4020 caldacs */
static int caldac_i2c_write(struct comedi_device *dev,
			    unsigned int caldac_channel, unsigned int value)
{}

static void caldac_write(struct comedi_device *dev, unsigned int channel,
			 unsigned int value)
{}

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

static void ad8402_write(struct comedi_device *dev, unsigned int channel,
			 unsigned int value)
{}

/* for pci-das6402/16, channel 0 is analog input gain and channel 1 is offset */
static int cb_pcidas64_ad8402_insn_write(struct comedi_device *dev,
					 struct comedi_subdevice *s,
					 struct comedi_insn *insn,
					 unsigned int *data)
{}

static u16 read_eeprom(struct comedi_device *dev, u8 address)
{}

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

/* Allocate and initialize the subdevice structures. */
static int setup_subdevices(struct comedi_device *dev)
{}

static int auto_attach(struct comedi_device *dev,
		       unsigned long context)
{}

static void detach(struct comedi_device *dev)
{}

static struct comedi_driver cb_pcidas64_driver =;

static int cb_pcidas64_pci_probe(struct pci_dev *dev,
				 const struct pci_device_id *id)
{}

static const struct pci_device_id cb_pcidas64_pci_table[] =;
MODULE_DEVICE_TABLE(pci, cb_pcidas64_pci_table);

static struct pci_driver cb_pcidas64_pci_driver =;
module_comedi_pci_driver();

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