/* base/src/drv $Id$ */

/*
 *
 * 	driver for at5 designed VXI modules
 *
 * 	Author:      Jeff Hill
 * 	Date:        11-89
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * 	Modification Log:
 * 	-----------------
 *	.01 joh 021490	first release
 *	.02 joh 040490	took out init of binary outs as requested by AT5
 *	.03 joh 040490	KLUDGED so DC is invarient of model number
 *	.04 joh 072590	fixed case where a missing SC module could 
 *			be accessed before checking to prevent bus error
 *	.05 joh 102990	slightly improved sync to busy in init
 *	.06 joh 032191	added code to implement a new register 
 *			interface from AT5
 *	.07 joh 080291	disable ints during control X reboot
 *	.08 joh 080291	fixed ints on before handler installed problem
 *			introduced by .06
 *	.09 joh 080291	synch source with sorce release control version
 *	.10 joh 080891	delinting
 *	.11 joh 090591	converted to v5 vxWorks
 *	.12 joh 120591	reorganized for use with new vxi support and removed 
 *			KLUDGE introduced in .03
 *	.13 joh 042492	removed support for (ifdefs for) the old
 *			style register map
 *	.14 joh 071792	added model name registration
 *	.15 joh 072992	print more raw values in io report
 *	.16 joh 081092	merged at5vxi_models.h into this source
 *	.17 joh 081992	function name change	
 *	.18 joh 082792	converted to ansi C
 *	.19 joh	111392	removed shifts on analog IO
 *	.20 joh	071593	fixed comment	
 *	.21 joh	081193	took out EPICS_V2 compile switches
 *
 *	Notes:
 *	------
 *	.01	Dont use C bitfields to write the AT5VXI CSR 
 *		directly because some bits have different meanings 
 *		for read than for write (bitfields writes generate RMW 
 *		instructions- performing reads with C bitfields 
 *		from an IO module works fine however).
 *
 *		To avoid this I write all bits in the CSR with
 *		each write. See the codes defined by at5vxi_init().
 */

/*
 *	Improvements:
 *
 *	Dont allow them to connect an interrupt if another device is
 *	installed there ! Perhaps intConnectSafe() should be written?
 */



/*
 * 	Code Portions
 *
 * 	AT5VXI_INIT		initialize all at5vxi cards
 * 	AT5VXI_INIT_CARD	initialize single at5vxi card
 *	AT5VXI_INT_SERVICE	update card busy writes, notify IO scanner
 *	AT5VXI_ONE_SHOT		setup AMD9513 STC with a one shot 
 *	AT5VXI_ONE_SHOT_READ	read back timing from an AMD 9513 STC 
 *	AT5VXI_STAT		print status for a single at5 vxi card
 *	AT5VXI_READ_TEST	diagnostic
 *	AT5VXI_AI_DRIVER	analog input driver	
 *	AT5VXI_AO_DRIVER	analog output driver
 *	AT5VXI_AO_READ		analog output read back
 *	AT5VXI_BI_DRIVER	binary input driver
 *	AT5VXI_BO_DRIVER	binary output driver
 *
 *
 */

#include <vxWorks.h>
#include <iv.h>
#include <types.h>
#include <rebootLib.h>
#include <sysLib.h>
#include <intLib.h>
#include <logLib.h>
#include <vxLib.h>
#include <stdioLib.h>

#include <devLib.h>
#include <module_types.h>
#include <task_params.h>
#include <fast_lock.h>
#include <drvSup.h>
#include <dbDefs.h>
#include <dbScan.h>
#include <drvEpvxi.h>
#include <drvAt5Vxi.h>
#include <drvStc.h>

static char SccsId[] = "@(#)drvAt5Vxi.c	1.19\t8/27/93";

typedef long	at5VxiStatus;

LOCAL void 	at5vxi_int_service(
	int 	addr
);

LOCAL void 	at5vxi_init_card(
	unsigned 	addr
);

LOCAL int	at5vxi_shutdown(void);

LOCAL void 	at5vxi_shutdown_card(
	unsigned la
);

LOCAL at5VxiStatus at5vxi_report_timing(
	unsigned card,
	unsigned channel
);

LOCAL void 	at5vxi_stat(
	unsigned	card,
	int		level
);

/*
 * these should be in a header file
 */
LOCAL at5VxiStatus at5vxi_init(
	void
);

at5VxiStatus	at5vxi_one_shot(	
	int		preset,		/* TRUE or COMPLEMENT logic */
	double		edge0_delay,	/* sec */
	double		edge1_delay,	/* set */
	unsigned	card,		/* 0 through ... */
	unsigned	channel,	/* 0 through channels on a card */
	int		int_source, 	/* (FALSE)External/(TRUE)Internal source */
	void		*event_rtn,	/* subroutine to run on events */
	int		event_rtn_param	/* parameter to pass to above routine */
);

at5VxiStatus 	at5vxi_one_shot_read(
	int		*preset,	/* TRUE or COMPLEMENT logic */
	double		*edge0_delay,	/* sec */
	double		*edge1_delay,	/* sec */
	unsigned	card,		/* 0 through ... */
	unsigned	channel,	/* 0 through channels on a card */
	int		*int_source 	/* (FALSE)External/(TRUE)Internal src */
);

at5VxiStatus	at5vxi_ai_driver(
	unsigned 	card,
	unsigned 	chan,
	unsigned short 	*prval
);

at5VxiStatus	at5vxi_ao_driver(
	unsigned 	card,
	unsigned 	chan,
	unsigned short	*prval,
	unsigned short 	*prbval
);

at5VxiStatus	at5vxi_ao_read(
	unsigned 	card,
	unsigned 	chan,
	unsigned short 	*pval
);

at5VxiStatus	at5vxi_bi_driver(
	unsigned 	card,
	unsigned long 	mask,
	unsigned long	*prval
);

at5VxiStatus	at5vxi_bo_driver(
	unsigned 	card,
	unsigned long	val,
	unsigned long	mask
);

at5VxiStatus at5vxi_getioscanpvt(
	unsigned 	card,
	IOSCANPVT 	*scanpvt
);

struct {
	long    number;
	DRVSUPFUN       report;
	DRVSUPFUN       init;
} drvAt5Vxi={
        2,
	NULL,			/* VXI io report takes care of this */
	at5vxi_init};

#define EXT_TICKS 5.0e06	/* GTA std speed of SRC1 in Hz	 */
	
/*
 *	Set this flag if you wish for the driver to
 *	to disable the busy period and operate
 *	without periodic interrupts
 *
#define CONTINUOUS_OPERATION
 *
 */

/*
 * all AT5VXI cards will use this VME interrupt level
 */
#ifdef CONTINUOUS_OPERATION
#	define AT5VXI_INT_LEVEL 	0
#	define AT5VXI_INT_ENABLE 	FALSE	
#	define AT5VXI_BUSY_ENABLE 	FALSE	
#else
#	define AT5VXI_INT_LEVEL 	5
#	define AT5VXI_INT_ENABLE 	TRUE
#	define AT5VXI_BUSY_ENABLE 	TRUE
#endif

#define abort(A)	taskSuspend(0)

/* 
 *	bit fields allocated starting with the ms bit
 */
struct at5vxi_status{
	unsigned	pad0:1;
	unsigned	modid_cmpl:1;
	unsigned	dev255:1;
	unsigned	busy:1;
	unsigned	pad1:2;
	unsigned	timer_bank:1;
	unsigned	ipend:1;
	unsigned	ienable:1;
	unsigned	ilevel:3;
	unsigned	ready:1;
	unsigned	passed:1;
	unsigned	sfinh:1;
	unsigned	sreset:1;
};


#define PSTATUS(PCSR)\
(&((struct vxi_csr *)PCSR)->dir.r.status)

struct at5vxi_control{
	unsigned	pad0:3;
	unsigned	busy_enable:1;
	unsigned	pad1:2;
	unsigned	timer_bank:1;
	unsigned	softint:1;
	unsigned	ienable:1;
	unsigned	ilevel:3;
	unsigned	pad2:2;
	unsigned	sfinh:1;
	unsigned	sreset:1;
};

/*
 * Insert or extract a bit field using the standard
 * masks and shifts defined below
 */
#ifdef __STDC__
#	define INSERT(FIELD,VALUE)\
	(((VALUE)&(FD_ ## FIELD ## _M))<<(FD_ ## FIELD ## _S))
#	define EXTRACT(FIELD,VALUE)\
	( ((VALUE)>>(FD_ ## FIELD ## _S)) &(FD_ ## FIELD ## _M))
#else
#	define INSERT(FIELD,VALUE)\
	(((VALUE)&(FD_/* */FIELD/* */_M))<<(FD_/* */FIELD/* */_S))
#	define EXTRACT(FIELD,VALUE)\
	( ((VALUE)>>(FD_/* */FIELD/* */_S)) &(FD_/* */FIELD/* */_M))
#endif

/*
 * in the constants below _M is a right justified mask
 * and _S is a shift required to right justify the field
 */
#define FD_INT_ENABLE_M		(0x1)
#define FD_INT_ENABLE_S		(7)
#define FD_BUSY_ENABLE_M	(0x1)
#define FD_BUSY_ENABLE_S	(12)
#define FD_BUSY_STATUS_M	(0x1)
#define FD_BUSY_STATUS_S	(12)
#define FD_INT_LEVEL_M		(0x7)
#define FD_INT_LEVEL_S		(4)
#define FD_TIMER_BANK_M		(0x1)
#define FD_TIMER_BANK_S		(9)

#define BUSY(PCSR)\
(((struct vxi_csr *)PCSR)->dir.r.status & INSERT(BUSY_STATUS,1))

/*
 * Some constants for the CSR. 
 *
 */
#ifndef CONTINUOUS_OPERATION
#	define INTDISABLE\
	( \
	INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \
	INSERT(INT_ENABLE, FALSE) | \
	INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \
	)
#endif

/* 
 * Used to initialize the control register. 
 * (enables interrupts)
 */
#define CSRINIT\
	( \
	INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \
	INSERT(INT_ENABLE, AT5VXI_INT_ENABLE) | \
	INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \
	)

/* 
 * Use bank zero of the timing. 
 */
#define BANK0\
	( \
	INSERT(TIMER_BANK, 0) | \
	INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \
	INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \
	)
/* 
 * Use bank one of the timing. 
 */
#define BANK1\
	( \
	INSERT(TIMER_BANK, 1) | \
	INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \
	INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \
	)

/* 
 * Some constants for the CSR. 
 * set at initialization for better readability
 */
#define PCONTROL(PCSR)\
(&((struct vxi_csr *)PCSR)->dir.w.control)




#define AT5VXI_TIMER_BANKS_PER_MODULE 2
#define AT5VXI_CHANNELS_PER_TIMER_BANK 5
#define AT5VXI_NTIMER_CHANNELS\
	(AT5VXI_TIMER_BANKS_PER_MODULE*AT5VXI_CHANNELS_PER_TIMER_BANK)


struct at5vxi_dd{
	vxi16_t		bio[2];
	vxi16_t		tdata;
	vxi8_t		pad;
	vxi8_t		tcmd;
	vxi16_t		ai[8];
	vxi16_t		ao[16];
};


struct at5vxi_setup{
#	define		UNITY	0
#	define		TIMES2	1
	unsigned	gainA:1;
	unsigned	gainB:1;
	unsigned	gainC:1;
	unsigned	gainD:1;
#	define		UNIPOLAR 0
#	define		BIPOLAR 1
	unsigned	modeA:1;
	unsigned	modeB:1;
	unsigned	modeC:1;
	unsigned	modeD:1;
	unsigned	ch2enbl:1;
	unsigned	ch2select:3;
	unsigned	ch1enbl:1;
	unsigned	ch1select:3;
};


#define AT5VXI_BUSY_PERIOD 2

struct bo_val {
	volatile int32_t	val;
	volatile int32_t	mask;
};

struct ao_val {
	volatile int16_t	mdt;
	volatile int16_t	val;
};

struct time_val {
	volatile unsigned	preset;		
	volatile int16_t	iedge0_delay;
	volatile int16_t	iedge1_delay;
	volatile char		mdt;
	volatile char		valid;
};

struct at5vxi_config{
	FAST_LOCK		lock;		/* mutual exclusion	*/
	struct bo_val		bv;		/* binary out values	*/
	struct ao_val		av[16];		/* analog out values	*/
	struct time_val		tv[10];		/* delayed pulse values	*/
	volatile char		mdt;		/* modified data tag	*/
	struct vxi_csr		*pcsr;		/* vxi device hdr ptr 	*/
	struct at5vxi_dd	*pdd;		/* at5 device dep ptr	*/
       	IOSCANPVT 		ioscanpvt;
};

LOCAL unsigned long at5vxiDriverID;

#define AT5VXI_PCONFIG(CARD, PTR) \
epvxiFetchPConfig(CARD, at5vxiDriverID, PTR)	

#define AT5VXI_CORRECT_MAKE(PCSR) 	(VXIMAKE(PCSR)==VXI_MAKE_AT5)

struct  at5vxi_model{
        char            *name;          /* AT5 VXI module name */
        char            *drawing;       /* AT5 VXI assembly drawing number */
};

#define AT5VXI_INDEX_FROM_MODEL(MODEL) ((unsigned)((MODEL)&0xff))
#define AT5VXI_MODEL_FROM_INDEX(INDEX) ((unsigned)((INDEX)|0xf00))

/*
        NOTE: The macro AT5VXI_INDEX_FROM_MODEL(MODEL) defined above
        should return an index into the correct data given the
        VXI device's model code.
*/
struct at5vxi_model at5vxi_models[] = {
        {"INTERFACE SIMULATOR",         "112Y-280158"},
        {"I CONTROLLER",                "112Y-280176"},
        {"CONTROL PREDISTORTER",        "112Y-280172"},
        {"VECTOR DETECTOR",             "112Y-280230"},
        {"VECTOR MODULATOR",            "112Y-280177"},
        {"425MHz ENVELOPE DETECTOR",    "112Y-280169"},
        {"425MHz DOWNCONVERTER",        "112Y-280165"},
        {"POLAR DETECTOR",              "112Y-280567"},
        {"UPCONVERTER",                 "112Y-280225"},
        {"MONITOR TRANSMITTER",         "112Y-280187"},
        {"TIMING DISTRIBUTION",         "112Y-280582"},
        {"LINE CONDITIONER",            "112Y-280305"},
        {"BEAM FEEDFORWARD",            "112Y-280564"},
        {"TIMING RECEIVER",             "112Y-280243"},
        {"FAST PROTECTION",             "112Y-280246"},
        {"ADAPTIVE FEEDFORWARD",        "112Y-280563"},
        {"CABLE CONTROLLER",            "112Y-280307"},
        {"Q CONTROLLER",                "112Y-280180"},
        {"ENVELOPE DETECTOR",           "112Y-280249"},
        {"DOWNCONVERTER",               "112Y-280456"},
        {"COAX MONITOR TRANSMITTER",    "112Y-280587"},
        {"CAVITY SIMULATOR",            "112Y-280232"},
};

#define AT5VXI_VALID_MODEL(MODEL) \
(AT5VXI_INDEX_FROM_MODEL(MODEL)<NELEMENTS(at5vxi_models))



/*
 * AT5VXI_INIT
 *
 * initialize all at5vxi cards
 *
 */
at5VxiStatus at5vxi_init(void)
{
	at5VxiStatus	r0;

	/*
	 * do nothing on crates without VXI
	 */
	if(!epvxiResourceMangerOK){
		return VXI_SUCCESS;
	}

	r0 = rebootHookAdd(at5vxi_shutdown);
	if(r0){
		errMessage(S_epvxi_internal, "rebootHookAdd() failed");
	}

	at5vxiDriverID = epvxiUniqueDriverID();

	{
		epvxiDeviceSearchPattern  dsp;

		dsp.flags = VXI_DSP_make;
		dsp.make = VXI_MAKE_AT5;
		r0 = epvxiLookupLA(&dsp, at5vxi_init_card, (void *)NULL);
		if(r0){
			return r0;
		} 
	}

	return VXI_SUCCESS;
}



/*
 * AT5VXI_SHUTDOWN
 *
 * disable interrupts on at5vxi cards
 *
 */
LOCAL int	at5vxi_shutdown(void)
{
	epvxiDeviceSearchPattern 	dsp;
	at5VxiStatus			s;

        dsp.flags = VXI_DSP_make;
        dsp.make = VXI_MAKE_AT5;
        s = epvxiLookupLA(&dsp, at5vxi_shutdown_card, (void *)NULL);
        if(s){
		errMessage(s,"AT5VXI module shutdown failed");
	}

	return OK;
}



/*
 * AT5VXI_SHUTDOWN_CARD
 *
 * disable interrupts on at5vxi cards
 *
 */
LOCAL 
void 	at5vxi_shutdown_card(
	unsigned 	la
)
{
#ifndef CONTINUOUS_OPERATION
	struct vxi_csr	*pcsr;

	pcsr = VXIBASE(la);

	pcsr->dir.w.control = INTDISABLE;
#endif
}



/*
 * AT5VXI_INIT_CARD
 *
 * initialize single at5vxi card
 *
 */
LOCAL 
void 	at5vxi_init_card(
	unsigned 		addr
)
{
	at5VxiStatus		r0;
	struct at5vxi_config	*pc;
	struct time_val  	*ptv;
	unsigned		chan;
	int			i;
	int 			model;

	r0 = epvxiOpen(
		addr, 
		at5vxiDriverID, 
		(unsigned long) sizeof(*pc),
		at5vxi_stat);
	if(r0){
		errPrintf(
			r0,
			__FILE__,
			__LINE__,
			"AT5VXI: device open failed %d\n", addr);
		return;
	}

	r0 = AT5VXI_PCONFIG(addr, pc);
	if(r0){
		errMessage(r0, NULL);
		epvxiClose(addr, at5vxiDriverID);
		return;
	}

	pc->pcsr = VXIBASE(addr);
	pc->pdd = (struct at5vxi_dd *) &pc->pcsr->dir.r.dd;

	FASTLOCKINIT(&pc->lock);

        scanIoInit(&pc->ioscanpvt);


	/*
	 * revert to power up control 
	 * (temporarily disable the busy period)
	 */
	pc->pcsr->dir.w.control = 0;


#ifndef CONTINUOUS_OPERATION
	/*
	 * wait 5 sec for the end of current busy cycle as required
	 * (busy period is temporarily disabled
	 *
	 */
	for(i=0; i<5 && BUSY(pc->pcsr); i++)
		taskDelay(sysClkRateGet());
	if(BUSY(pc->pcsr)){
		epvxiClose(addr, at5vxiDriverID);
		return;
	}
#endif

#if defined(INIT_BINARY_OUTS)
	/*
	 * Set AD 664 default
	 */
	{
		struct at5vxi_setup su;

		su.gainA = TIMES2;
		su.gainB = TIMES2;
		su.gainC = TIMES2;
		su.gainD = TIMES2;

		su.modeA = BIPOLAR;
		su.modeB = BIPOLAR;
		su.modeC = BIPOLAR;
		su.modeD = BIPOLAR;

		su.ch2enbl = FALSE;
		su.ch1enbl = FALSE;

		(* (struct at5vxi_setup *) &pc->pdd->bio[1]) = su;
	}
#endif

	/*
	 * Init AMD 9513 for
	 * 
	 * binary division 
	 * data ptr seq enbl 
	 * 16 bit bus 
 	 * FOUT on 
	 * FOUT divide by 16
	 * FOUT source (F1) 
	 * Time of day disabled
	 */
#	define MASTER_MODE ((uint16_t)0x2000)
	*PCONTROL(pc->pcsr) = BANK0;
	r0 = stc_init(	&pc->pdd->tcmd, 
			&pc->pdd->tdata, 
			MASTER_MODE);
	if(r0!=STC_SUCCESS){
		epvxiClose(addr, at5vxiDriverID);
		return;
	}

	*PCONTROL(pc->pcsr) = BANK1;
	r0 = stc_init(
		&pc->pdd->tcmd, 
		&pc->pdd->tdata, 
		MASTER_MODE);
	if(r0!=STC_SUCCESS){	
		epvxiClose(addr, at5vxiDriverID);
		return;
	}

	for(chan=0, ptv = pc->tv; chan<NELEMENTS(pc->tv); chan++, ptv++){
		unsigned int_source;

		if(chan/AT5VXI_CHANNELS_PER_TIMER_BANK){
			*PCONTROL(pc->pcsr) = BANK1;
		}
		else{
			*PCONTROL(pc->pcsr) = BANK0;
		}

		/*
		 * casting below discards volatile
		 * (ok in this case)
		 */
		r0 = stc_one_shot_read(
			(unsigned *)&ptv->preset, 
			(uint16_t *)&ptv->iedge0_delay, 
			(uint16_t *)&ptv->iedge1_delay,	
			&pc->pdd->tcmd, 
			&pc->pdd->tdata, 
			chan,
			&int_source);
		if(r0 == STC_SUCCESS && int_source == FALSE)
			ptv->valid = TRUE;
		else
			ptv->valid = FALSE;
	}

#	ifndef CONTINUOUS_OPERATION
  		r0 = intConnect(
			INUM_TO_IVEC(addr), 
			at5vxi_int_service, 
			addr);
  		if(r0 == ERROR)
    			return;

		sysIntEnable(AT5VXI_INT_LEVEL);
#	endif

	/*
	 * init the csr
	 * (see at5vxi_init() for field definitions)
	 * interrupts enabled if not compiled for continuous operation
	 */
	*PCONTROL(pc->pcsr) = CSRINIT;

	model = VXIMODEL(pc->pcsr);	
	if(AT5VXI_VALID_MODEL(model)){
		r0 = epvxiRegisterModelName(
			VXIMAKE(pc->pcsr), 
			model, 
			at5vxi_models[AT5VXI_INDEX_FROM_MODEL(model)].name);
		if(r0){
			errMessage(r0,NULL);
		}

		r0 = epvxiRegisterMakeName(VXI_MAKE_AT5, "LANL AT5");
		if(r0){
			errMessage(r0,NULL);
		}
	}

	return;
}


/*
 *
 *	AT5VXI_INT_SERVICE
 *
 *	update card busy writes and notify the IO interrupt scanner
 */
void 	at5vxi_int_service(
	int 	addr
)
{
	struct at5vxi_config 	*pconfig; 
	at5VxiStatus		r0;

	r0 = AT5VXI_PCONFIG(addr, pconfig);
	if(r0){
		logMsg(	"AT5VXI: int before init\n",
			NULL,
			NULL,
			NULL,
			NULL,
			NULL,
			NULL);
		return;
	}

	/*
	 * wake up the I/O event scanner
	 */
	scanIoRequest(pconfig->ioscanpvt);

	/*
	 * Update outputs while it is safe to do so
	 */
	if(pconfig->mdt){
		struct at5vxi_dd	*pdd;
		struct vxi_csr		*pcsr; 
		unsigned 		chan;

		pcsr = pconfig->pcsr;
		pdd = pconfig->pdd;

		for(chan=0; chan<NELEMENTS(pconfig->tv); chan++){
			unsigned chip_chan;

			if(!pconfig->tv[chan].mdt)
				continue;


			if(chan/AT5VXI_CHANNELS_PER_TIMER_BANK){
				*PCONTROL(pcsr) = BANK1;
			}
			else{
				*PCONTROL(pcsr) = BANK0;
			}

 			chip_chan = chan%
				AT5VXI_CHANNELS_PER_TIMER_BANK;

 			r0 = stc_one_shot(
				pconfig->tv[chan].preset, 
				pconfig->tv[chan].iedge0_delay, 
				pconfig->tv[chan].iedge1_delay, 
				&pdd->tcmd, 
				&pdd->tdata, 
				chip_chan,
				FALSE);
			if(r0 != STC_SUCCESS){
				logMsg(	"AT5 VXI- AMD9513 load fail\n",
					NULL,
					NULL,
					NULL,
					NULL,
					NULL,
					NULL);
			}
			else{
				pconfig->tv[chan].valid = TRUE;
			}

			/*
			 * reenable interrupts
			 */
			*PCONTROL(pcsr) = CSRINIT;

			pconfig->tv[chan].mdt = FALSE;
		}

		for(chan=0; chan<NELEMENTS(pconfig->av); chan++){
			if(!pconfig->av[chan].mdt)
				continue;
			pdd->ao[chan] = pconfig->av[chan].val;
			pconfig->av[chan].mdt = FALSE;
		}

		if(pconfig->bv.mask){
			uint32_t	work;

			work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | 
					pdd->bio[0]);

			/* alter specified bits */
			work = (work & ~pconfig->bv.mask) | 
				(pconfig->bv.val & pconfig->bv.mask);

			pdd->bio[0] =  work;
			pdd->bio[1] =  work>>(NBBY*sizeof(uint16_t));

			pconfig->bv.mask = 0;
		}

		if(BUSY(pcsr))
			logMsg(	"AT5 VXI INT- finished with card busy\n",
				NULL,
				NULL,
				NULL,
				NULL,
				NULL,
				NULL);

		pconfig->mdt = FALSE;
	}

}



/*
 *
 *	AT5VXI_ONE_SHOT
 *
 *	setup AMD 9513 STC for a repeated two edge timing signal 
 */
at5VxiStatus	at5vxi_one_shot(	
	int		preset,		/* TRUE or COMPLEMENT logic */
	double		edge0_delay,	/* sec */
	double		edge1_delay,	/* set */
	unsigned	card,		/* 0 through ... */
	unsigned	channel,	/* 0 through channels on a card */
	int		int_source, 	/* (FALSE)External/(TRUE)Internal source */
	void		*event_rtn,	/* subroutine to run on events */
	int		event_rtn_param	/* parameter to pass to above routine */
)
{
  	at5VxiStatus			status;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;

	status = AT5VXI_PCONFIG(card, pconfig);
	if(status){
		return status;
	}

	pcsr = pconfig->pcsr;

/*	AT5VXI does not support internal source for now
*/	if(int_source){
		status = S_dev_badRequest;
		errMessage(
			status, 
			"AT5VXI does not support internal trigger source");
		return status;
	}

/*	AT5VXI does not support interrupts on timing channels for now
*/	if(event_rtn){
		status = S_dev_badRequest;
		errMessage(status, "AT5VXI does not support interrupts on timing channels");
		return status;
	}

	if(channel>=AT5VXI_NTIMER_CHANNELS)
		return S_dev_badSignalNumber;

/* 	dont overflow unsigned short in STC
*/  	if(edge0_delay >= devCreateMask(NBBY*sizeof(uint16_t))/EXT_TICKS)
    		return S_dev_highValue;
  	if(edge1_delay >= devCreateMask(NBBY*sizeof(uint16_t))/EXT_TICKS)
    		return S_dev_highValue;
  	if(edge0_delay < 0.0)
    		return S_dev_lowValue;
  	if(edge1_delay < 0.0)
    		return S_dev_lowValue;

	FASTLOCK(&pconfig->lock);

#	ifdef CONTINUOUS_OPERATION
	{
		struct at5vxi_dd	*pdd;

		pdd = pconfig->pdd;

		if(channel/AT5VXI_CHANNELS_PER_TIMER_BANK){
			*PCONTROL(pcsr) = BANK1;
		}else{
			*PCONTROL(pcsr) = BANK0;
		}
		channel = channel%AT5VXI_CHANNELS_PER_TIMER_BANK;

  		status = stc_one_shot(
			preset, 
			(uint16_t) (edge0_delay * EXT_TICKS), 
			(uint16_t) (edge1_delay * EXT_TICKS), 
			&pdd->tcmd, 
			&pdd->tdata, 
			channel,
			FALSE);

		/*
		 * not required for now but safe
		 * against future mods
		 */
		*PCONTROL(pcsr) = CSRINIT;
	}
#	else
		*PCONTROL(pcsr) = INTDISABLE;
		pconfig->tv[channel].preset = preset;
		pconfig->tv[channel].iedge0_delay = 
				(edge0_delay * EXT_TICKS);
		pconfig->tv[channel].iedge1_delay = 
				(edge1_delay * EXT_TICKS);	
		pconfig->tv[channel].mdt = TRUE;		
		pconfig->mdt = TRUE;	
		*PCONTROL(pcsr) = CSRINIT;
	
		status = STC_SUCCESS;
#	endif

	FASTUNLOCK(&pconfig->lock);

	if(status!=STC_SUCCESS){
		return status;
	}

	return VXI_SUCCESS;
}


/*
 *
 *	AT5VXI_ONE_SHOT_READ
 *
 *	read back two edge timing from an AMD 9513 STC
 */
at5VxiStatus	at5vxi_one_shot_read(
	int		*preset,	/* TRUE or COMPLEMENT logic */
	double		*edge0_delay,	/* sec */
	double		*edge1_delay,	/* sec */
	unsigned	card,		/* 0 through ... */
	unsigned	channel,	/* 0 through channels on a card */
	int		*int_source 	/* (FALSE)External/(TRUE)Internal src */
)
{	
#ifdef CONTINUOUS_OPERATION
  	uint16_t	iedge0;
  	uint16_t	iedge1;
#endif
	at5VxiStatus			status;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;

	status = AT5VXI_PCONFIG(card, pconfig);
	if(status)
		return status;

	pcsr = pconfig->pcsr;

	if(channel>=AT5VXI_NTIMER_CHANNELS)
		return S_dev_badSignalNumber;


#	ifdef CONTINUOUS_OPERATION
	{
		struct at5vxi_dd	*pdd;

		pdd = pconfig->pdd;

		FASTLOCK(&pconfig->lock);

		if(channel/AT5VXI_CHANNELS_PER_TIMER_BANK){
			*PCONTROL(pcsr) = BANK1;
		}else{
			*PCONTROL(pcsr) = BANK0;
		}
		channel = channel%AT5VXI_CHANNELS_PER_TIMER_BANK;
		status = stc_one_shot_read(
				preset, 
				&iedge0, 
				&iedge1, 
				&pdd->tcmd, 
				&pdd->tdata, 
				channel,
				int_source);
		/*
		 * not required for noe but safe
		 * against future mods
		 */
		*PCONTROL(pcsr) = CSRINIT;
		FASTUNLOCK(&pconfig->lock);

  		if(status==STC_SUCCESS){
			/*
			 * AT5VXI does not support external 
			 * source for now
			 */	
			if(int_source)
				return S_dev_badRequest;

    			*edge0_delay = iedge0 / EXT_TICKS;
    			*edge1_delay = iedge1 / EXT_TICKS;
  		}


  		return status;
	}
#	else
		if(!pconfig->tv[channel].valid)
			return S_dev_badRequest;

		FASTLOCK(&pconfig->lock);

		*preset = pconfig->tv[channel].preset;
		*edge0_delay = pconfig->tv[channel].iedge0_delay
					/EXT_TICKS;
		*edge1_delay = pconfig->tv[channel].iedge1_delay
					/EXT_TICKS;	
		*int_source = FALSE;

		FASTUNLOCK(&pconfig->lock);

		return VXI_SUCCESS;
#	endif
}



/*
 *
 *	AT5VXI_STAT
 *
 *	print status for a single at5 vxi card
 *	
 *
 */
void 	at5vxi_stat(
	unsigned	card,
	int		level
)
{
	struct vxi_csr			*pcsr; 
	register struct at5vxi_dd	*pdd;
	struct at5vxi_status		status;
	unsigned			channel;
	at5VxiStatus			r0;
	struct at5vxi_config 		*pconfig;

	static char  	*busy_status[] = 	{"","busy"};
	static char  	*modid_status[] =	{"modid-on",""};
	static char  	*ipend_status[] =	{"","int-pending"};
	static char  	*ienable_status[] =	{"int-disabled","int-enabled"};
	static char  	*ext_st_status[] =	{"extended-self-testing",""};
	static char  	*st_status[] =		{"self-testing",""};
	static char  	*sfinh_status[] =	{"","sys-fail-inhibit"};
	static char  	*sreset_status[] =	{"","sys-reset"};
	static char  	*addr_mode_status[] =	{"SC","DC"};

	if(level==0)
		return;

	r0 = AT5VXI_PCONFIG(card, pconfig);
	if(r0){
		errMessage(r0,NULL);
		return;
	}

	pcsr = VXIBASE(card);
	pdd = pconfig->pdd;

  	r0 = vxMemProbe(	(char *)&pcsr->dir.r.status,
				READ,
				sizeof(status),
				(char *)&status);
  	if(r0 != OK)
    	  	return;

	if(VXIMAKE(pcsr) != VXI_MAKE_AT5)
		return;

	if(AT5VXI_VALID_MODEL(VXIMODEL(pcsr))){
		printf( "\tDrawing: %s\n",
			at5vxi_models[AT5VXI_INDEX_FROM_MODEL(VXIMODEL(pcsr))].drawing);
	}

  	printf(	
		"\tcard=%d address-mode=%s %s %s %s %s ilevel=%d %s %s %s %s\n",
		card,
		addr_mode_status[status.dev255],
		busy_status[ status.busy ],
		modid_status[ status.modid_cmpl ],
		ipend_status[ status.ipend ],
		ienable_status[ status.ienable ], 
		status.ilevel,
		ext_st_status[ status.ready ],
		st_status[ status.passed ],
		sfinh_status[ status.sfinh ],
		sreset_status[ status.sreset ]);

	if(pconfig){
		if(pconfig->mdt){
			printf("\toutput update is pending for interrupt\n");
		}
	}
 
	if(level <= 1)
		return;

	for(channel=0; channel<NELEMENTS(pdd->ai); channel++){
		printf(
			"\tAI:  channel %d value %x\n",
			channel,
			pdd->ai[channel]);
	}

	for(channel=0; channel<NELEMENTS(pdd->ao); channel++){
		printf(
			"\tAO:  channel %d value %x\n",
			channel,
			pdd->ao[channel]);
	}

	{
		uint32_t	work;

		work = ((uint32_t)pdd->bio[1]) << (sizeof(uint16_t)*NBBY);	
		work |= pdd->bio[0];
		printf("\tBIO: value %x\n", work);
	}

	for(channel=0; channel<NELEMENTS(pconfig->tv); channel++){
		at5vxi_report_timing(card, channel);
	}

	return;
}


/*
 *
 *
 *	AT5VXI_REPORT_TIMING
 *
 *	diagnostic
 */
LOCAL 
at5VxiStatus at5vxi_report_timing(
	unsigned card,
	unsigned channel
)
{
  int 		preset;
  double 	edge0_delay;
  double 	edge1_delay;
  int 		int_source;
  at5VxiStatus	status;
  char		*clk_src[] = {"external-clk", "internal-clk"};

  status = 
    at5vxi_one_shot_read(	
			&preset,
			&edge0_delay,
			&edge1_delay, 
			card, 
			channel,
			&int_source);
  if(status == VXI_SUCCESS)
    printf(
	"\tTI:  channel %d preset %d delay %f width %f %s\n",
		channel,
		preset,
		edge0_delay,
		edge1_delay, 
		clk_src[int_source?1:0]);
		
  return status;
}


/*
 *
 *
 *	AT5VXI_AI_DRIVER
 *
 *	analog input driver
 */
at5VxiStatus	at5vxi_ai_driver(
	unsigned 	card,
	unsigned 	chan,
	unsigned short 	*prval
)
{
	at5VxiStatus			s;
	register struct at5vxi_dd	*pdd;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;

	s = AT5VXI_PCONFIG(card, pconfig);
	if(s)
		return s;

	pcsr = pconfig->pcsr;
	pdd = pconfig->pdd;

	if(chan >= NELEMENTS(pdd->ai))
		return S_dev_badSignalNumber;

	*prval = pdd->ai[chan];

	return VXI_SUCCESS;
}


/*
 *
 *
 *	AT5VXI_AO_DRIVER
 *
 *	analog output driver
 */
at5VxiStatus  at5vxi_ao_driver(
	unsigned 	card,
	unsigned 	chan,
	unsigned short 	*prval,
	unsigned short 	*prbval
)
{
	struct at5vxi_dd		*pdd;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;
	at5VxiStatus			s;

	s = AT5VXI_PCONFIG(card, pconfig);
	if(s)
		return s;

	pcsr = pconfig->pcsr;
	pdd = pconfig->pdd;

	if(chan >= NELEMENTS(pdd->ao))
		return S_dev_badSignalNumber;

#ifdef CONTINUOUS_OPERATION
	pdd->ao[chan] = *prval;
	*prbval = pdd->ao[chan];
#else
	*PCONTROL(pcsr) = INTDISABLE;
	pconfig->av[chan].val = *prval;
	pconfig->av[chan].mdt = TRUE;		
	pconfig->mdt = TRUE;	
	*PCONTROL(pcsr) = CSRINIT;

	*prbval = *prval;
#endif
	return VXI_SUCCESS;
}


/*
 *
 *
 *	AT5VXI_AO_READ
 *
 *	analog output read back
 */
at5VxiStatus at5vxi_ao_read(
	unsigned 	card,
	unsigned 	chan,
	unsigned short 	*pval
)
{
	register struct at5vxi_dd	*pdd;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;
	at5VxiStatus			s;

	s = AT5VXI_PCONFIG(card, pconfig);
	if(s){
		return s;
	}

	pcsr = pconfig->pcsr;
	pdd = pconfig->pdd;

	if(chan >= NELEMENTS(pdd->ao))
		return S_dev_badSignalNumber;

	*pval = pdd->ao[chan];

	return VXI_SUCCESS;
}


/*
 *
 *
 *	AT5VXI_BI_DRIVER
 *
 *	binary input driver
 */
at5VxiStatus at5vxi_bi_driver(
	unsigned 	card,
	unsigned long	mask,
	unsigned long	*prval
)
{
	register uint32_t		work;
	register struct at5vxi_dd	*pdd;
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;
	at5VxiStatus			s;

	s = AT5VXI_PCONFIG(card, pconfig);
	if(s)
		return s;

	pcsr = pconfig->pcsr;
	pdd = pconfig->pdd;

	FASTLOCK(&pconfig->lock);
	work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | pdd->bio[0]);
	*prval = mask & work;
	FASTUNLOCK(&pconfig->lock);

	return VXI_SUCCESS;
}


/*
 *
 *
 *	AT5VXI_BO_DRIVER
 *
 *	binary output driver
 */
at5VxiStatus at5vxi_bo_driver(
	unsigned 	card,
	unsigned long	val,
	unsigned long	mask
)
{
#ifdef CONTINUOUS_OPERATION
	register uint32_t	work;
#endif
  	register struct vxi_csr		*pcsr;
	register struct at5vxi_config 	*pconfig;
	at5VxiStatus			s;

	s = AT5VXI_PCONFIG(card, pconfig);
	if(s)
		return s;

	pcsr = pconfig->pcsr;

	FASTLOCK(&pconfig->lock);

#ifdef CONTINUOUS_OPERATION
	{
		struct at5vxi_dd	*pdd;

		pdd = pconfig->pdd;

		work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | pdd->bio[0]);

		/* alter specified bits */
		work = (work & ~mask) | (val & mask);

		pdd->bio[0] =  work;
		pdd->bio[1] =  work>>(NBBY*sizeof(uint16_t));
	}
#else
	*PCONTROL(pcsr) = INTDISABLE;
	pconfig->bv.val = (pconfig->bv.val & ~mask) | (val & mask);
	pconfig->bv.mask |= mask;
	pconfig->mdt = TRUE;	
	*PCONTROL(pcsr) = CSRINIT;
#endif

	FASTUNLOCK(&pconfig->lock);
	
	return VXI_SUCCESS;
}


/*
 *
 * at5vxi_getioscanpvt()
 *
 *
 */
at5VxiStatus at5vxi_getioscanpvt(
unsigned 	card,
IOSCANPVT 	*scanpvt
)
{
	struct at5vxi_config   *pconfig;
	at5VxiStatus		s;

        s = AT5VXI_PCONFIG(card, pconfig);
        if(s == VXI_SUCCESS){
		*scanpvt = pconfig->ioscanpvt;
	}
        return s;
}

