/* -*-C-*-
*******************************************************************************
*
* File:         mc4020.h
* Description:  Measurement Computing PCI-DAS4020/12 driver
*
*******************************************************************************
*/
/*
 * Copyright 2001 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * This driver is conceptually based on the MIT SpectrumWare Guppi driver.
 * I have attempted to preserve as much of the external interface and 
 * behavior as possible including ioctl codes.
 *
 * This driver is somewhat unconventional in that it has no read or write
 * entry points.  All data is transfered by mmaping the kernel owned
 * buffers into the user address space.  The kernel and user
 * communicate about page usage and availability using the GIOCSETGETSTATUS 
 * ioctl.
 *
 * This driver handles input only, and is capable of an aggregate input
 * rate of 80MB/second across the PCI bus.  This corresponds to two channels
 * at 20 MSPS, or 4 channels at 10 MSPS.  We typically are using only 1 channel 
 * at 20 MSPS, for an average throughput of 40MB/sec.
 *
 * We're input only because of a hardware restriction.  The 4020/12 is
 * only capable of polled analog output with software controlled
 * pacing.  This is unacceptable for our needs, hence we punt.  
 *
 * References:
 *
 *  PCI-DAS4020/12  Analog & Digital I/O Board User's Manual
 *	http://www.measurementcomputing.com/PDFManuals/pci-das4020-12.pdf
 *
 *  PLX 9080 PCI Data Book version 1.06
 *	http://www.plxtech.com/download/9080/databook/9080db-106.pdf
 */

#ifndef _MC4020_H_
#define _MC4020_H_


#include <linux/ioctl.h>

/*
 * --------------------------------------------------------------------
 *			user visible items
 * --------------------------------------------------------------------
 */

/*!
 * \brief argument to the mc4020 GIOCSETGETSTATUS ioctl
 *
 * mc4020_status is passed to and returned from the GIOCSETGETSTATUS ioctl.
 *
 * On input to ioctl:
 *
 *	INDEX is the page index (from the beginning the mmap region) to the
 *	first page in use by the application.  On the first call, it should
 *	be zero.
 *
 *	NUM is the number of pages starting at INDEX being returned
 *	to the driver.  On the first call, it should be zero.
 *
 *	LOST is ignored.
 *
 * On output to ioctl:
 *
 *	INDEX is the page index to the first page in use by the application.
 *
 *	NUM is the number of pages currently valid in the buffer starting
 *	at INDEX.
 *
 *	LOST is non-zero if an overrun occurred.
 */

struct mc4020_status {
  unsigned int index; // index of the first valid page (this page is valid if num > 0)
  unsigned int num;   // number of valid pages at index
  unsigned int lost;  // non-zero if an overrun occurred
};

/*!
 * \brief argument to the mc4020 GIOCSETCONFIG ioctl
 *
 * configures sampling rate, range and selected inputs for mc4020
 */
struct mc4020_config {
  unsigned long		bitmask;	// see below
  unsigned long		scan_rate;	// scans / second
};

// bitmask values for mc4020_config.bitmask

#define	MCC_CH0_EN		 0x00000001	// enable channel 0 for input
#define	MCC_CH1_EN		 0x00000002	
#define	MCC_CH2_EN		 0x00000004
#define	MCC_CH3_EN		 0x00000008
#define	MCC_CH0_5V		 0x00000010	// ch0 range: +/- 5V
#define	MCC_CH0_1V		 0x00000000	// ch0 range: +/- 1V
#define	MCC_CH1_5V		 0x00000020	// ch1 range: +/- 5V
#define	MCC_CH1_1V		 0x00000000	// ch1 range: +/- 1V
#define	MCC_CH2_5V		 0x00000040	// ch2 range: +/- 5V
#define	MCC_CH2_1V		 0x00000000	// ch2 range: +/- 1V
#define	MCC_CH3_5V		 0x00000080	// ch3 range: +/- 5V
#define	MCC_CH3_1V		 0x00000000	// ch3 range: +/- 1V
// select input source
#define	MCC_ASRC_MASK		 0x00000700
#define	MCC_ASRC_BNC		 0x00000000	// BNC connector (normal)
#define	MCC_ASRC_CAL_AGND	 0x00000100	// AGND
#define	MCC_ASRC_CAL_0_625	 0x00000200	// 0.625 V ref
#define	MCC_ASRC_CAL_4_375	 0x00000300	// 4.375 V ref
#define	MCC_ASRC_CAL_HDR	 0x00000400	// calibration header (P2)
// Trig/Ext Clk BNC threshold select
#define	MCC_EXT_BNC_THRESH_ZERO	 0x00000800
#define	MCC_EXT_BNC_THRESH_2_5V	 0x00000000
// data acquistion base clock source
#define	MCC_CLK_MASK		 0x00003000
#define	MCC_CLK_INTERNAL	 0x00000000	// 40MHz internal clock
#define	MCC_CLK_EXT_BNC	 	 0x00001000	// external BNC connector
#define	MCC_CLK_AD_START_TRIG_IN 0x00002000	// external A/D start trigger in pin
 
#define	MCC_ALL_1V		(MCC_CH0_1V | MCC_CH1_1V | MCC_CH2_1V | MCC_CH3_1V)
#define	MCC_ALL_5V		(MCC_CH0_5V | MCC_CH1_5V | MCC_CH2_5V | MCC_CH3_5V)

struct mc4020_caldac {
  unsigned short	index;
  unsigned short	value;
};

struct mc4020_caldata {
  float		 cal_4_375;		// actual value of 4.375V ref
  float		 cal_0_625;		// actual value of 0.625V ref
  unsigned short cal_data[16];		// 0: ADC0 +5V gain
					// 1: ADC0 +5V offset
					// 2: ADC0 +1V gain
					// 3: ADC0 +1V offset
					// 4: ADC1 +5V gain
					// and so on...
};

/*
 * constraints on buffer sizes
 */
#define	MCBUF_LOG2_MULTIPLE	20				// 2^20 bytes
#define	MCBUF_MULTIPLE		(1L << MCBUF_LOG2_MULTIPLE)	// in bytes
#define	MCBUF_MINIMUM		MCBUF_MULTIPLE			// in bytes

/*
 * mc4020 IOCTL commands
 *   note a file descriptor must be for reading OR writing, not both
 *   so we can use the same codes for start/stop, etc.
 */

/*
 * Set standard buffer size to some number of bytes.
 * size must be a multiple of MCBUF_MULTIPLE and >= MCBUF_MINIMUM
 */
#define GIOCSETBUFSIZE  _IOW ('G', 0x00, unsigned int)

/* Start up the DMA/interrupt driver */
#define GIOCSTART _IO ('G', 0x01)

/* Stop the DMA/interrupt driver */
#define GIOCSTOP  _IO ('G', 0x02)


/* Tell the driver the user is done with a range of pages and get the */
/*  range of pages the user can use as well as the number of overruns */
#define GIOCSETGETSTATUS _IOWR ('G', 0x03, struct mc4020_status)

/* likewise, only don't wait for pages if not available */
#define GIOCSETGETSTATUS_NOWAIT _IOWR ('G', 0x04, struct mc4020_status)


/* Configure the board for acquisition */
#define	GIOCSETCONFIG _IOW ('G', 0x05, struct mc4020_config)


/* read calibration data from EEPROM */
#define	GIOCGETCALDATA _IOR ('G', 0x06, struct mc4020_caldata)


/*
 * mc4020 IOCTL commands for auxilliary devices:
 *
 *	DAC0 and DAC1 -- 12 bit digital to analog converters
 *	8255	      -- 24 pins of digital i/o
 *
 * Note, these IOCTLs are also valid for the ADC subdevice
 */

/* enable (true) or disable (false) DACs based on bool argument */
#define	GIOCENABLEDACS _IOW('G', 0x07, int)


/* set output range for DAC */

#define	MCDAC_RANGE_5V		0x1	/* +/-  5V range	*/
#define	MCDAC_RANGE_10V		0x0	/* +/- 10V range	*/

#define	GIOCSETDAC0RANGE _IOW('G', 0x08, int)
#define	GIOCSETDAC1RANGE _IOW('G', 0x09, int)

/* get output range for DAC */

#define	GIOCGETDAC0RANGE _IOR('G', 0xa, int *)
#define	GIOCGETDAC1RANGE _IOR('G', 0xb, int *)

/*
 * Write value to DAC
 * The DAC is an Analog Devices AD7237 dual 12-bit DAC.
 *
 * Here is the mapping from value to output voltage:
 *
 * Range	Input Value	Output Voltage
 *
 * +/- 10V	0x0000		-10.000   V
 * +/- 10V	0x0800		  0.000   V
 * +/- 10V      0x0FFF		 +9.99513 V
 *
 * +/- 5V	0x0000		 -5.000   V
 * +/- 5V	0x0800		  0.000   V
 * +/- 5V       0x0FFF		 +4.99756 V
 */
#define	GIOCWRITEDAC0 _IOW('G', 0x0c, int)
#define	GIOCWRITEDAC1 _IOW('G', 0x0d, int)


/*
 * MC4020 major device number 
 */

#define MC4020_MAJOR  127

/*
 * The MC4020 minor number is formatted as follows:
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |       zero          |A| board |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * where A is set if this is the auxillary subdevice.
 *
 * If A == 0, then this device provides access to the high speed
 *   analog to digital converters and the DACs and 8255.
 *
 * If A == 1, then this device provides access to ONLY the
 *   DACS and 8255.
 *
 * The ADC subdevice is exclusive open (only one process at a time).
 * The AUX subdevice can be opened by an arbitrary number
 * of processes.
 */

/*
 * ----------------------------------------------------------------
 *			kernel only
 * ----------------------------------------------------------------
 */
#ifdef __KERNEL__

#define	SUBDEV_ADC	0	// ADCs, DACs, 8255 subdevice
#define	SUBDEV_OTHER	1	// DACs, 8255 only subdev

#include <linux/pci.h>
#include <linux/spinlock.h>


#ifndef   PCI_VENDOR_ID_COMPUTER_BOARDS
#define   PCI_VENDOR_ID_COMPUTER_BOARDS	0x1307
#endif

#ifndef   PCI_DEVICE_ID_PCI_DAS_4020_12
#define   PCI_DEVICE_ID_PCI_DAS_4020_12	0x0052
#endif

/*
 * --------------------------------------------------------------------
 * We use three regions of memory:
 *
 *	BAR0:	plx 9080 control registers
 *	BAR1:	i/o mapped version of BAR0 (I think)
 *	BAR2:	pci-das4020/12 16-bit registers
 *	BAR3:	pci-das4020/12 32-bit fifo
 * --------------------------------------------------------------------
 */

#include "plx9080.h"

/*
 * --------------------------------------------------------------------
 * BAR2 (aka Local Space 0) registers.
 *
 * These are the control and status registers for the pci-das4020/12.
 * MC4020_<foo>_OFF are byte offsets from the base of the region.
 * These registers are all 16-bits wide.
 * --------------------------------------------------------------------
 */

/*
 * =====================
 * write only registers
 * =====================
 */
#define	MC4020_INTR_ENABLE_OFF			0x00

#define   IE_OVERRUN			(1 << 15)	// overrun can be detected (sets bit in stat reg)
#define	  IE_DAQ_STOP			(1 << 10)	// intr on TRIG2 (stop trigger)
#define	  IE_DAQ_ACTIVE			(1 <<  9)	// intr when DAQ sequence is active
#define	  IE_XINT			(1 <<  8)	// if set, external intr input can generate intr
#define	  IE_DAQ_DONE			(1 <<  3)	// intr when DAQ sequence completes (or overruns)
#define	  IE_DAQ_IENB			(1 <<  2)	// if set, one of _ISRC_ conds generates intr
#define	  IE_ISRC_MASK			     0x3
#define	  IE_ISRC_FIFO_HALF_FULL	     0x0	// intr when fifo half full
#define	  IE_ISRC_SINGLE_CONV		     0x1	// intr when sample is in fifo
#define	  IE_ISRC_PACED			     0x2	// intr each sample during paced conv
#define	  IE_ISRC_RESERVED		     0x3	// (not used)


#define	MC4020_HW_CONF_OFF			0x02

#define	  HWC_XINT_EDGE_MASK		(1 << 8)
#define	  HWC_XINT_EDGE_RISING		      0
#define	  HWC_XINT_EDGE_FALLING	    HWC_XINT_EDGE_MASK
#define	  HWC_WCLK_MASK			    0x3
#define	  HWC_WCLK_INACTIVE		    0x0
#define	  HWC_WCLK_INTERNAL_40MHZ	    0x1
#define	  HWC_WCLK_EXT_CLK_BNC		    0x2
#define	  HWC_WCLK_AD_START_TRIG_IN	    0x3


#define	MC4020_FIFO_SIZE_OFF			0x04

#define	  FSIZE_64K			   0x00		// size in samples
#define	  FSIZE_32K			   0x40
#define	  FSIZE_16K			   0x60
#define	  FSIZE_8K			   0x70
#define	  FSIZE_4K			   0x78
#define	  FSIZE_2K			   0x7c
#define	  FSIZE_1K			   0x7e
#define	  FSIZE_512			   0x7f
#define	  FSIZE_MAX		      FSIZE_64K


#define	MC4020_DAQ_ATRIG_LO_OFF			0x0c	// analog trigger low threshold

#define	  ATLO_XAGATE_SRC		(1 << 15)	// FIXME docs are inconsistent on top 3 bits
#define	  ATLO_XTRIG2_SRC		(1 << 14)
#define	  ATLO_XTRIG1_SRC		(1 << 13)
#define	  ATLO_THRESH_MASK		  0x0fff


#define	MC4020_DAQ_ATRIG_HI_OFF			0x0e	// analog trigger high threshold

#define	  ATHI_THRESH_MASK		  0x0fff


#define	MC4020_DAQ_CR0_OFF			0x10	// DAQ control register 0

#define   CR0_DAQ_ENB			(1 << 15)	// master enable
#define	  CR0_DMA_DSBL			(1 << 14)	// set to disable plx demand mode dma (see manual)
#define	  CR0_reserved_13		(1 << 13)
#define	  CR0_SAMPCNT_ENB		(1 << 12)	// Sample counter enable.  used for pre/post trig
#define	  CR0_reserved_11		(1 << 11)
#define	  CR0_reserved_10		(1 << 10)

#define	  CR0_TRIG2_ENB			(1 <<  9)	// TRIG2 pre-trigger enable
#define	  CR0_TRIG2_EDGE_MASK		(1 <<  8)
#define	  CR0_TRIG2_EDGE_RISING		0
#define	  CR0_TRIG2_EDGE_FALLING	CR0_TRIG2_EDGE_MASK
#define	  CR0_TRIG2_SRC_MASK		(1 <<  7)	
#define	  CR0_TRIG2_SRC_EXT_STOP_PIN	0
#define	  CR0_TRIG2_SRC_ANALOG		CR0_TRIG2_SRC_MASK

#define	  CR0_TRIG1_EDGE_MASK		(1 <<  6)
#define	  CR0_TRIG1_EDGE_RISING		0
#define	  CR0_TRIG1_EDGE_FALLING	CR0_TRIG1_EDGE_MASK
#define	  CR0_TRIG1_SRC_MASK		(0x3 << 4) 	// AGATE must be active for start trig to be recognized
#define	  CR0_TRIG1_SRC_DISABLED	(0x0 << 4)
#define	  CR0_TRIG1_SRC_SOFTWARE	(0x1 << 4)
#define	  CR0_TRIG1_SRC_EXTERNAL_TRIG1	(0x2 << 4)
#define	  CR0_TRIG1_SRC_ANALOG		(0x3 << 4)

#define	  CR0_AGATE_POL_MASK		(1 <<  3)
#define	  CR0_AGATE_POL_HIGH		0
#define	  CR0_AGATE_POL_LOW		CR0_AGATE_POL_MASK
#define	  CR0_AGATE_TYPE_MASK		(1 <<  2)
#define	  CR0_AGATE_TYPE_EDGE_SENS	0
#define	  CR0_AGATE_TYPE_LEVEL_SENS	CR0_AGATE_TYPE_MASK
#define	  CR0_AGATE_SRC_MASK		(0x3 << 0)
#define	  CR0_AGATE_SRC_DISABLED	(0x0 << 0)
#define	  CR0_AGATE_SRC_SOFTWARE	(0x1 << 0)	// set state using SFT_GATE in CR1
#define	  CR0_AGATE_SRC_EXTERNAL	(0x2 << 0)	// ATLO_XAGATE_SRC specifies actual external src
#define	  CR0_AGATE_SRC_ANALOG		(0x3 << 0)	// see also CR1_ATRIG_SRC, CR1_ATRIG_MD


#define	MC4020_DAQ_CR1_OFF			0x12	// DAQ control register 1

#define	  CR1_PSC_ENB			(1 << 15)	// divide by 400 prescaler enable
#define	  CR1_reserved_14		(1 << 14)

#define	  CR1_CHANMODE_MASK		(0x3 << 12)
#define	  CR1_CHANMODE_1		(0x0 << 12)	// 20MHz max sample rate on 1 channel
#define	  CR1_CHANMODE_2		(0x1 << 12)	// 20MHz max sample rate on 2 channels
#define	  CR1_CHANMODE_4		(0x2 << 12)	// 10MHz max sample rate on 4 channels
#define	  CR1_CHANMODE_4_BURST		(0x3 << 12)	// 20MHz max sample rate; 32K samples / channel max

#define	  CR1_UCHAN_MASK		(0x3 << 10)	// upper channel select in 2 channel mode
#define	  CR1_UCHAN(x)			(((x) & 0x3) << 10)
#define	  CR1_UCHAN_0			(0x0 << 10)	// channel 0	
#define	  CR1_UCHAN_1			(0x1 << 10)	// channel 1
#define	  CR1_UCHAN_2			(0x2 << 10)	// channel 2
#define	  CR1_UCHAN_3			(0x3 << 10)	// channel 3

#define	  CR1_LCHAN_MASK		(0x3 <<  8)	// lower channel select in 1 and 2 channel mode
#define	  CR1_LCHAN(x)			(((x) & 0x3) << 8)
#define	  CR1_LCHAN_0			(0x0 <<  8)	// channel 0	
#define	  CR1_LCHAN_1			(0x1 <<  8)	// channel 1
#define	  CR1_LCHAN_2			(0x2 <<  8)	// channel 2
#define	  CR1_LCHAN_3			(0x3 <<  8)	// channel 3
#define	  CR1_reserved_7		(1 <<  7)
#define	  CR1_SFT_GATE			(1 <<  6)	// set enables ADC conversions, see CR0_AGATE_SRC_SOFTWARE

#define	  CR1_ATRIG_SRC_MASK		(0x3 << 4)	// analog trigger source channel...
#define	  CR1_ATRIG_SRC_0		(0x0 << 4)	// channel 0
#define	  CR1_ATRIG_SRC_1		(0x1 << 4)
#define	  CR1_ATRIG_SRC_2		(0x2 << 4)
#define	  CR1_ATRIG_SRC_3		(0x3 << 4)

#define	  CR1_ATRIG_MD_MASK		(0x7 << 1)	// Analog trigger/gate modes
#define	  CR1_ATRIG_MD_INACTIVE		(0x0 << 1)
#define	  CR1_ATRIG_MD_POS_HYST		(0x2 << 1)
#define	  CR1_ATRIG_MD_NEG_HYST		(0x3 << 1)
#define	  CR1_ATRIG_MD_NEG_SLOPE	(0x4 << 1)
#define	  CR1_ATRIG_MD_POS_SLOPE	(0x5 << 1)
#define	  CR1_ATRIG_MD_WINDOW		(0x6 << 1)

#define	  CR1_reserved_0		(1 <<  0)


// ADC frequency = base_clock / (divisor + 2)

#define	MC4020_DAQ_SAMPLE_INTERVAL_LO_OFF	0x16	// 16 low bits of sample interval divisor
#define	MC4020_DAQ_SAMPLE_INTERVAL_HI_OFF	0x18	// 8 high bits of sample interval divisor (right justified)


#define MC4020_DAQ_COUNT_LO_OFF			0x1e	// 16 low bits of sample count reg
#define	MC4020_DAQ_COUNT_HI_OFF			0x20	// 8 high bits of sample count reg (right justified)


// Writing MC4020_SOFT_START initiates a multple A/D conversion data
// acquisition operation.
//
// To trigger the board with this register, CR0_TRIG1_SRC_SOFTWARE
// must be set and the AGATE must be active too.  For an all software
// solution, I believe that you must enable CR0_AGATE_SRC_SOFTWARE and
// CR1_SFT_GATE, then write MC4020_SOFT_START.

#define	MC4020_SOFT_START_OFF			0x22	


// Command register that causes a single conversion to take place.
// Result is in the fifo.  The docs say something confusing about having to
// program the divisor prior to issuing this command. YMMV

#define	MC4020_SINGLE_CONV_OFF			0x24

#define	  SC_CHAN_0			(0x0 <<  8)	// read channel 0
#define	  SC_CHAN_1			(0x1 <<  8)	// read channel 1...
#define	  SC_CHAN_2			(0x2 <<  8)
#define	  SC_CHAN_3			(0x3 <<  8)


// Writing the BUFFER_PTR_CLEAR register resets the fifo pointer to the home state
// and clears the internal pipeline registers (PIPE2, PIPE1, PIPE0).

#define	MC4020_BUFFER_PTR_CLEAR_OFF		0x2a


// Digital to Analog Converter control registers

#define	MC4020_DAC_CR1_OFF			0x52

#define	  DAC_CR1_DAC_OE		(1 <<  7)	// set to enable DAC outputs
#define	  DAC_CR1_DAC1_RANGE_BIP10V	(0 <<  2)	// DAC1 +/- 10V range
#define	  DAC_CR1_DAC1_RANGE_BIP5V	(1 <<  2)	// DAC1 +/- 5V range
#define	  DAC_CR1_DAC0_RANGE_BIP10V	(0 <<  0)	// DAC0 +/- 10V range
#define	  DAC_CR1_DAC0_RANGE_BIP5V	(1 <<  0)	// DAC0 +/- 5V range

// Data format is offset binary.
//
// E.g., on the +/- 10V scale:
//
//	0x000	-> -10.000V
//	0x800	->   0.000V
//	0xfff	->  +9.99513
//
// Write 8 low bits, then 4 high bits

#define	MC4020_DAC0_DATA_LO_OFF			0x70	// 8 low bits, right justified
#define	MC4020_DAC0_DATA_HI_OFF			0x72	// 4 high bits, right justified

#define	MC4020_DAC1_DATA_LO_OFF			0x74	// likewise...
#define	MC4020_DAC1_DATA_HI_OFF			0x76

/*
 * ====================
 * read only registers
 * ====================
 */
#define	MC4020_HW_STATUS_OFF			0x00	// all pending interupts generated by the STC's
							// interrupt control logic are serviced by
							// reading the HW_STATUS register.
#define	  HWS_REV_MASK			(0x7 << 13)
#define	  HWS_NEXT_CHAN			(1   << 12)	// clear if last sample was even, else odd
#define	  HWS_PIPEFULL_MASK		(0x3 << 10)	// status of internal pipeline registers
#define	  HWS_TRIG2_FLG			(1   <<  9)	// if set, stop trigger intr is pending
#define	  HWS_XINT_FLG			(1   <<  8)	// if set, external interrupt is pending
#define	  HWS_DAQ_DONE			(1   <<  7)	// if set, DAQ operation is complete
#define	  HWS_reserved_6		(1   <<  6)
#define	  HWS_ASRC_FLG			(1   <<  5)	// if set, one of _ISRC_ ints is pending
							//   also ADC_BUSY status after single convert cmd
#define	  HWS_reserved_4		(1   <<  4)
#define	  HWS_DAQ_ACTIVE		(1   <<  3)	// current DAQ operation is active
#define	  HWS_reserved_2		(1   <<  2)
#define	  HWS_DAQ_OVERRUN		(1   <<  1)	// overrun error was detected.
#define	  HWS_reserved_0		(1   <<  0)


// The ADC_READ_PTR register provides the address of the fifo read pointer.
// The read pointer is used to determine the amount of residual data that
// resides in the on-card fifo when a data acquisition has completed.
// (WRITE_PTR - READ_PTR) = number of samples (sample pairs?) left in the fifo.

#define	MC4020_ADC_READ_PTR_REG_OFF		0x08

// The ADC_WRITE_PTR register provides the address of the fifo write pointer.
// The write pointer is used determine the amount of residual data that
// resides in the on-card fifo when a data acquisition has complete.  See above.

#define	MC4020_ADC_WRITE_PTR_REG_OFF		0x0c


// I think that the next two registers come into play when using pre-triggering.
// I think they allow you to determine where the trigger actually occurred.
//
// The USER_XFER_COUNT register provides the index into the current DMA chain.
// (Seems like this register isn't wide enough, and the units aren't clear either)

#define	MC4020_USER_XFER_COUNT_OFF		0x10

// [verbatim from the docs:]  Chain Flag bits.  These bits help determine what scatter-gather
// chain entry data was last DMA transferred to.  These bits can be compared to the LAD(4:3)
// bits programmed in the DMA Channel Local Address register to guarantee that the correct final
// chain entry has been located.

#define	MC4020_PRE_POST_REG_OFF			0x14

#define	  CHAIN_FLG1			(1 <<  7)
#define	  CHAIN_FLG2			(1 <<  6)


/*
 * =====================
 * Read/Write registers
 * =====================
 */

#define	MC4020_82C55_PORT_A			0x48
#define	MC4020_82C55_PORT_B			0x4a
#define	MC4020_82C55_PORT_C			0x4c
#define	MC4020_82C55_CTRL_PORT			0x4e

/*
 * ======================================================================
 *			  I2C & eeprom access
 *
 * The I2C bus is a two wire bus consisting of clock and data.  The data
 * is generally bidirectional, but in this implementation is output only.
 * The clock line is called SCL and the data line is SDA.
 *
 * SDA and SCLK are controlled with the PLX 9080 CNTRL register.
 * SCL is the USEROUT pin, SDA is the EEPROM write bit (shared with the
 * configuration EEPROM).
 *
 * On this card, the I2C bus consists of 3 devices, an 8 bit register 
 * (haven't figured out the part number) and two Analog Devices quad 10
 * bit dacs (AD5315).
 *
 * The factory calibration data is stored in the unused top of the
 * PCI configuration EEPROM starting at offset 0x30.  The EEPROM is
 * compatible with the Fairchild FM93CS56 part and uses the
 * "Microwire" 4-wire bus.  This bus is also controlled via the PLX 9080
 * CNTRL register.
 * ======================================================================
 */

/* bit defs in the CNTRL register */

#define	I2CBUS_SCL		PLX_USEROUT
#define	I2CBUS_SDA		PLX_EEWD

/* I2C devices */

#define	I2C_REGISTER_ADDR			0x20

#define	  IREG_THRESH_ZERO	(1 << 7)	// trig/ext clk BNC threshold
#define	  IREG_THRESH_TTL	(0 << 7)
						// selects analog source
#define	  IREG_ASRC_CAL_HDR	(0x0 << 4)	// Calibration header (P2)
#define	  IREG_ASRC_BNC	    	(0x4 << 4)	// BNC connector
#define	  IREG_ASRC_CAL_4_375   (0x5 << 4)	// 4.375 V ref
#define	  IREG_ASRC_CAL_0_625	(0x6 << 4)	// 0.625 V ref
#define   IREG_ASRC_CAL_AGND	(0x7 << 4)	// 0.000 V ref

#define	  IREG_ATTEN_CH3	(1 <<  3)	// attenuate input by 5:1
#define	  IREG_ATTEN_CH2	(1 <<  2)
#define	  IREG_ATTEN_CH1	(1 <<  1)
#define	  IREG_ATTEN_CH0	(1 <<  0)


#define	I2C_CAL_DAC0_ADDR			0x0c

#define	I2C_CAL_DAC1_ADDR			0x0d


/*
 * --------------------------------------------------------------------
 * BAR3 (aka Local Space 1) registers.
 *
 * The FIFO is mapped into this area...
 * MC4020_<foo>_OFF are byte offsets from the base of the region
 * --------------------------------------------------------------------
 */

#define	MC4020_BAR3_FIFO_OFF			0x200		// 32-bit register (2 samples wide)



/*
 * --------------------------------------------------------------------
 * 			mc4020 kernel structures
 * --------------------------------------------------------------------
 */

#define	BASE_CLK_FREQ	 40000000	// internal clock ref (40 MHz)

#define	MIN_SCAN_RATE		0	// FIXME get real value
#define	MAX_SCAN_RATE_1	 20000000	// 20 MSPS
#define	MAX_SCAN_RATE_2	 20000000	// 20 MSPS
#define	MAX_SCAN_RATE_4	 10000000	// 10 MSPS
	

// at 20M samples/sec we're burning 40MB / second.
// set 16MB as our default buffer size.  
// Those without memory need not apply...

#define	MCBUF_DEFAULT	(16*1024*1024)			// in bytes


struct mc4020_buf {
  int			npages;		// # of entries in next two vectors
  struct plx_dma_entry	*dma_entry;	// vector of logical addresses
  void			**dma_page;	// vector of logical addresses 
};

//
// which write only registers we shadow.
//
#define	MIN_SHADOW	MC4020_INTR_ENABLE_OFF
#define	MAX_SHADOW	MC4020_BUFFER_PTR_CLEAR_OFF
#define	NSHADOWS	(((MAX_SHADOW - MIN_SHADOW) / sizeof (u16)) + 1)

static inline int 
shadowed_p (int offset)
{
  return MIN_SHADOW <= offset && offset <= MAX_SHADOW;
}

// driver states
typedef enum {
  ST_CLOSED,			// device is closed
  ST_IDLE,			// device is open, ready for START ioctl
  ST_DMA_RUNNING,		// dma is running
  ST_DMA_STOPPED,		// dma is stopped (overrun), but would like to be running
} state_t;

struct   mc4020 {
  unsigned long		plx_vaddr;	// virtual addr of PLX regs
  unsigned long		reg_vaddr;	// virtual addr of PCI-DAS4020/12 regs
  unsigned long		fifo_vaddr;	// virtual addr of FIFO regs

  u16			shadow[NSHADOWS];	// STC register shadows
  u16			caldac_shadow[8];
  u16			i2c_reg_shadow;
  u16			dac_cr1_shadow;
  
  struct pci_dev       	*pdev;
  unsigned int         	minor;
  struct semaphore	sem;
  spinlock_t           	lock;
  wait_queue_head_t	wq;
  volatile state_t	state;

  int			expected_ints;	// bitmask of expect interrupts (HWS_xxx)
  volatile int		last_hw_status;	// diagnostic
  volatile long		last_plx_intcsr;// diagnostic

  
  struct mc4020_caldata	caldata;
  struct mc4020_config	config;
  int			nchannels; 	// # of channels enabled
  unsigned long		throughput;	// bytes / second
  int			chunksize;	// pages / irq;
  
  // stuff for receive starts here
  unsigned int		bufsize; 	//in pages
  struct mc4020_buf    	*buffer;

  //indicies are relative to beginning of buffer
  volatile unsigned int	fill_index;	// next page to be written
  unsigned int 		empty_index; 	// start of empty pages
  unsigned int 		user_index; 	// start of user's pages
  unsigned int 		user_num;  	// # of pages user knows about and is working with

};

#endif	// __KERNEL__

#endif /* _MC4020_H_ */
