Files
pcas/src/drv/drvDvx.c
1993-01-29 11:52:04 +00:00

1270 lines
34 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* share/src/drv/drvDvx.c
* share/src/drv $Id$
*
* subroutines which are used to interface with the analogic 2502
* A/D scanner cards
*
* Author: Matthew Stettler
* Date: 5-23-90
*
* AT-8 hardware design
*
* Modules:
*
* dvx_driver_init Finds and initializes all 2502 cards
* dvx_dma_init Initializes Am9516 DMA controller
* dvx_driver Reads data from 2502
* dvx_int Interrupt service routine
*
* Test Routines:
*
* dvx_dump dumps RAM buffer
* dvx_dread command line interface to dvx_driver
* dvx_fempty clears fifo from command line
* dvx_dma_stat displays DMA channel status
*
* 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:
* -----------------
* MS 6/22/90 Modifications to aid in debugging interface to Mizar timing
* system. Interrupt service routine now counts words when clearing
* fifo, dvx_dump provides fifo status, and dvx_fempty added to
* clear fifo from command line.
*
* MS 7/9/90 Modifications to speed up interrupt service routine.
*
* MS 7/23/90 Added DMA control logic.
*
* MS 8/20/90 Added support for interrupt scanned records.
*
* MS 9/20/90 Changed data conversion to offset binary
* (only test routines affected)
*
* JH 07/25/91 added dvx_reset() and dvx_reset() on control X reboot
*
* JH 11/14/91 changed a sysBCLSet to sysIntEnable so ioc_core
* will load into the nat inst cpu030
*
* JH 11/14/91 removed sysBCLSet enabling STD non priv and super
* access since this is already enabled if we are
* processor 0
* JH 11/14/91 changed DVX_INTLEV from a mask to a level
* to support use of sysIntEnable() which
* is architecture independent
* BG 4/22/92 added sysBusToLocalAddr() for both short and standard
* addresses for this module. Moved DVX_ADDR0 to
* ai_addrs[DVX2502]in module_types.h. Also moved DVX_IVECO
* to module_types.h.
* BG 6/23/92 combined dvx_driver.c and drvDvx.c
* BG 06/26/92 added level to dvx_io_report in drvDvx structure.
* JH 06/29/92 moved the rest of the dvx io_report here
* BG 7/2/92 removed the semaphores from dvx_io_report
* JH 08/03/92 Removed hkv2f dependent base addr
* JH 08/03/92 moved interrupt vector base to module_types.h
* JH 08/05/92 dvx driver init is called from drvDvx now
* JH 08/10/92 merged dvx private include file into this source
* JH 09/09/92 ran through UNIX C beautifier and converted to ANSI C
* JH 09/09/92 check to see if A24 is open prior to mapping
* the seq ram.
* JH 09/14/92 Made taskDelays() CPU tick rate independent
* tick rate
* JH 09/15/92 made interrupt vector CPU independent
* MRK 09/16/92 Added support for new I/O event scanning
* JH 09/16/92 dont write wordcnt every time the fifo is unloaded
* JH 09/16/92 Use sysLocalToBusAdrs() to translate from an internal
* to a VME A24 address in case the internal base for A24
* addressed local memory is not on a 16MB boundary.
* sysLocalToBusAdrs() is called for each malloc'ed
* pointer used by the DVX to verify that each one is
* within the portion of local memory mapped to A24.
* JH 09/17/92 one more sysLocalToBusAdrs() address translation
* needed
* JRW 01/18/92 Replaced init code to allow user to select the interrupt
* level value and to select the ports that are to be read.
*
*
* NOTE (JRW 11-18-92):
* In a phone conversation with Analogic, Tech support said that when the start
* signal is de-asserted (when using external start/stop mode), the DMAC is
* told to start transferring more data. This is in case the sampling
* completes with less than 512 bytes in the fifo.
*
* BUGS:
* The driver should inspect the VXI make and model codes and use a data type
* for the DMA buffer that is appropriate.
*/
static char *SccsId = "$Id$";
#include <vxWorks.h>
#include <stdioLib.h>
#include <vme.h>
#include <dbDefs.h>
#include <drvSup.h>
#include <module_types.h>
#ifdef V5_vxWorks
#include <iv.h>
#else
#include <iv68k.h>
#endif
#ifndef EPICS_V2
#include <dbScan.h>
#endif
/* general constants */
#define DVX_ID 0xCFF5 /* analogic ID code */
#define MAX_DVX_CARDS 5 /* max # of 2502 cards per IOC */
#define MAX_PORTS 3 /* max # of 2601 cards per 2502 */
#define DVX_DRATE 0xFFEC /* default scan rate of 184 KHz */
#define DVX_SRATE 0xF201 /* slow scan used for run away mode */
#define DVX_RAMSIZE 2048 /* sequence RAM size (words) */
#define DVX_NBUF 1 /* default # of input buffers */
/* modes of operation */
#define INIT_MODE 0 /* initialization mode */
#define RUN_MODE 1 /* aquisition mode */
/* self test constants */
#define TST_RATE 0x3ED /* self test scan rate */
#define TST_THRESH 0xD00 /* mux card test threshold reg value */
/* csr command bits */
#define CSR_RESET 0x01 /* software reset */
#define CSR_M_START 0x10 /* internal scan start */
#define CSR_M_ETRIG 0x40 /* external trigger enable */
#define CSR_M_ESTART 0x20 /* external start enable */
#define CSR_M_SYSFINH 0x02 /* system fail inhibit */
#define CSR_M_A24 0x8000 /* enable sequence RAM */
#define CSR_M_INT 0x80 /* interrupt enable */
#define CSR_M_MXTST 0x3A /* mux card test bits */
/* csr status bits */
#define S_NEPTY 0x02 /* fifo not empty when = 1 */
/* Sequence Program control codes */
#define GAIN_CHANNEL 0x80
#define ADVANCE_TRACK 0x40
#define ADVANCE_HOLD 0xC0
#define RESTART 0x00
/* analogic 2502 memory structure */
struct dvx_2502
{
unsigned short dev_id; /* device id code (CFF5) */
unsigned short dev_type; /* type code (B100) */
unsigned short csr; /* control and status register */
unsigned short seq_offst; /* sequence RAM offset register */
unsigned short mem_attr; /* memory attribute register */
unsigned short samp_rate; /* sample rate register */
unsigned short dma_point; /* DMA pointer register */
unsigned short dma_data; /* DMA data register */
unsigned short thresh; /* threshold register */
unsigned short fifo; /* input fifo */
unsigned short end_pad[54]; /* pad to 64 byte boundary */
};
/* input buffer */
struct dvx_inbuf
{
struct dvx_inbuf *link; /* link to next buffer */
int wordcnt; /* # of words read to clear fifo */
short *data; /* data buffer */
};
/* analogic 2502 control structure */
struct dvx_rec
{
struct dvx_2502 *pdvx2502; /* pointer to device registers */
short *sr_ptr; /* pointer to sequence RAM */
struct dvx_inbuf *inbuf; /* pointer to current buffer */
short unsigned csr_shadow; /* csr shadow register */
short mode; /* operation mode (init or run) */
int int_vector; /* interrupt vector */
int intcnt; /* interrupt count # */
int cnum; /* card number */
int dmaSize; /* samples to read before IRQ */
unsigned int numChan; /* total number of ports to read */
unsigned long pgmMask[8]; /* ports to be read by seq-program */
#ifndef EPICS_V2
IOSCANPVT *pioscanpvt;
#endif
};
/* dma chain table size */
#define DVX_CTBL 34 /* max size of chain table */
/* am9516 register select constants.
* The DMA control registers are accessed through the dvx2502 registers
* dma_point and dma_data. The constants below are the addresses which must
* be loaded into the pointer register to access the named register through
* the data register. All dual registers are commented as #2. To access channel
* #1, OR the value M_CH1 with the channel #2 address.
*/
#define DMA_MMR 0x38 /* master mode register */
#define DMA_CSR 0x2C /* command/status register #2 */
#define DMA_CARAH 0x18 /* current address reg A high #2 */
#define DMA_CARAL 0x08 /* current address reg A low #2 */
#define DMA_CARBH 0x10 /* current address reg B high #2 */
#define DMA_CARBL 0x00 /* current address reg B low #2 */
#define DMA_BARAH 0x1C /* base address reg A high #2 */
#define DMA_BARAL 0x0C /* base address reg A low #2 */
#define DMA_BARBH 0x14 /* base address reg B high #2 */
#define DMA_BARBL 0x04 /* base address reg B low #2 */
#define DMA_COC 0x30 /* current operation count #2 */
#define DMA_BOC 0x34 /* base operation count #2 */
#define DMA_ISR 0x28 /* interrupt save register #2 */
#define DMA_IVR 0x58 /* interrupt vector register #2 */
#define DMA_CMRH 0x54 /* channel mode register #2 */
#define DMA_CMRL 0x50 /* channel mode register #2 */
#define DMA_CARH 0x24 /* chain address reg high #2 */
#define DMA_CARL 0x20 /* chain address reg low #2 */
#define M_CH1 0x2 /* mask for chan 1 reg addresses */
/* am9516 command constants
* All dual commands are commented as #1. To command channel #2, OR the
* valur M_CH2 with the channel #1 command.
*/
#define MMR_ENABLE 0x0D /* chip enable value */
#define CMR_RESET 0x0 /* reset all channels */
#define CMR_START 0xA0 /* start channel #1 */
#define CMR_SSR 0x42 /* set software request #1 */
#define CMR_CSR 0x40 /* clear software request #1 */
#define CMR_SHM 0x82 /* set hardware mask #1 */
#define CMR_CHM 0x80 /* clear hardware mask #1 */
#define CMR_SC 0x22 /* set CIE/IP #1 */
#define CMR_CC 0x20 /* clear CIE/IP #1 */
#define CMR_SFB 0x62 /* set flip bit #1 */
#define CMR_CFB 0x60 /* clear flip bit #1 */
#define M_CIE 0x10 /* int enable bit mask (SC/CC cmd) */
#define M_IP 0x4 /* int pending bit mask (SC/CC cmd) */
#define M_CH2 0x1 /* mask for channel #2 commands */
/* am9516 chain reload constants */
#define R_CAR 0x1 /* chain address */
#define R_CMR 0x2 /* channel mode */
#define R_IVR 0x4 /* interrupt vector */
#define R_PMR 0x8 /* pattern and mask */
#define R_BOC 0x10 /* base operation count */
#define R_BAB 0x20 /* base address register B */
#define R_BAA 0x40 /* base address register A */
#define R_COC 0x80 /* current operation count */
#define R_CAB 0x100 /* current address register B */
#define R_CAA 0x200 /* current address register A */
/* If any of the following does not exist replace it with #define <> NULL */
long dvx_io_report(int level);
long dvx_driver_init(void);
struct {
long number;
DRVSUPFUN report;
DRVSUPFUN init;
} drvDvx={
2,
dvx_io_report,
dvx_driver_init};
static struct dvx_rec dvx[MAX_DVX_CARDS] = {
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
#ifndef EPICS_V2
, NULL
#endif
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
#ifndef EPICS_V2
, NULL
#endif
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
#ifndef EPICS_V2
, NULL
#endif
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
#ifndef EPICS_V2
, NULL
#endif
}
};
static int DVX_INTLEV=5; /* allow this to be user setable */
static int dvxOnline = 0; /* 1 after init invoked */
int lclToA24(void *pLocal, void **ppA24);
void dvx_reset(void);
void dvx_int(struct dvx_rec *dvxptr);
int muxtst(int card);
int sramld(int card);
int dvx_driver( int card, int chan, short *pval);
int dvx_dread(int card,int chan);
int dvx_dump(int card,int firstchan,int lastchan);
int dvx_chan_print(int dvx_card, int firstchan, int lastchan);
int dvx_fempty(int card);
int dvx_dma_init(struct dvx_rec *ptr);
int dvx_dma_reset(struct dvx_rec *ptr);
int dvx_dma_stat(int card, int chan);
int dvxDebug = 0;
/*
* dvx_int
*
* interrupt service routine
*
*/
LOCAL void
dvx_int(struct dvx_rec *dvxptr)
{
static short i, junk;
register struct dvx_2502 *cptr;
static unsigned int tick, t, intlev;
cptr = dvxptr->pdvx2502;
cptr->dma_point = DMA_CSR;
cptr->dma_data = CMR_CC | M_IP | M_CH2; /* clear dma int channel #2 */
cptr->csr = dvxptr->csr_shadow & 0xff5f; /* disable fifo interrupts */
switch (dvxptr->mode)
{
/*
* interrupt recieved during initialization
* - empty fifo and throw away data
*/
case INIT_MODE:
if(dvxDebug)
logMsg("dvx_int: INIT_MODE\n");
dvxptr->intcnt = 0; /* initialize interrupt count */
for (i = 0; cptr->csr & 0x2; i++)
junk = cptr->fifo;
break;
/*
* interrupt recieved during data aquisition
* - empty fifo into next input buffer, then make it current
*/
case RUN_MODE:
if(dvxDebug)
logMsg("dvx_int: RUN_MODE\n");
dvxptr->intcnt++; /* incriment interrupt count */
dvxptr->inbuf = dvxptr->inbuf->link; /* update current data buffer */
for (i = 0; cptr->csr & S_NEPTY; i++, junk = cptr->fifo);
dvxptr->inbuf->wordcnt = i; /* record # of words to clear fifo */
/* enable DMA opeations */
cptr->dma_point = DMA_CSR;
cptr->dma_data = CMR_SC | M_CIE | M_CH2; /* enable int channel #2 */
cptr->dma_data = CMR_START | M_CH2; /* start channel #2 */
#ifdef EPICS_V2
io_scanner_wakeup(IO_AI,DVX2502,dvxptr->cnum); /*update database records */
#else
scanIoRequest(*(dvxptr->pioscanpvt));
#endif
break;
}
cptr->csr = dvxptr->csr_shadow;
}
/*
* dvx_driver_init
*
* initialization for 2502 cards
*
*/
LOCAL long dvx_driver_init(void)
{
int i;
int j;
int status;
unsigned short card_id;
short *ramptr;
struct dvx_inbuf *ibptr;
struct dvx_inbuf *ibptra;
int intvec = DVX_IVEC0;
struct dvx_2502 *pDvxA16;
short *pDvxA24;
short *pDvxA24Bus;
/*
* dont continue DMA while vxWorks is control X
* rebooting (and changing bus arbitration mode)
* joh 072591
*/
rebootHookAdd(dvx_reset);
dvxOnline = 1; /* do not allow any more user config modifications */
status = sysBusToLocalAdrs(
VME_AM_SUP_SHORT_IO,
ai_addrs[DVX2502],
&pDvxA16);
if (status != OK){
logMsg( "%s: A16 base addr problems DVX 2502\n",
__FILE__);
return ERROR;
}
/*
* search for DVX cards
*/
for ( i = 0, pDvxA24Bus = (short *)ai_memaddrs[DVX2502];
i < ai_num_cards[DVX2502];
i++, pDvxA16++, pDvxA24Bus += DVX_RAMSIZE)
{
# ifdef DEBUG
logMsg("Probing for DVX at %x\n", pDvxA16);
# endif
dvx[i].pdvx2502 = NULL;
status = vxMemProbe (
&pDvxA16->dev_id,
READ,
sizeof(card_id),
&card_id);
if (status <0){
continue;
}
if (card_id != DVX_ID){ /* see if detected card is a 2502 */
logMsg("%s: Card installed at addr=0X%x is not a dvx2502\n",
__FILE__,
pDvxA16);
continue;
}
/* Card found! Finish the init for it. */
dvx[i].cnum = i; /* record card number */
pDvxA16->csr = CSR_RESET; /* software reset */
status = sysBusToLocalAdrs(
VME_AM_STD_SUP_DATA,
pDvxA24Bus,
&pDvxA24);
if (status != OK){
logMsg( "%s: A24 base addr problems DVX 2502 A24=%x\n",
__FILE__,
pDvxA24Bus);
continue;
}
/*
* check for incorrectly addressed card in A24
*/
status = vxMemProbe (pDvxA24, READ, sizeof(card_id), &card_id);
if (status == OK){
logMsg( "%s: A24 responding where DVX should be addr=%x\n",
__FILE__,
pDvxA24);
logMsg( "%s: DVX card=%d ignored\n",
__FILE__,
i);
continue;
}
#ifndef EPICS_V2
if ((dvx[i].pioscanpvt = (IOSCANPVT *) malloc(sizeof(IOSCANPVT))) == NULL)
return(-1);
#endif
if (dvx[i].dmaSize == 0)
{
logMsg("%s: No channels selected on card %d, init aborted\n", i);
continue;
}
dvx[i].mode = INIT_MODE; /* initialization mode */
/* create linked list of input buffers */
for ( j = 0, ibptra = NULL;
j < DVX_NBUF;
j++, ibptra = ibptr)
{
ibptr = (struct dvx_inbuf *)malloc(sizeof (struct dvx_inbuf));
/*
* exit with error if buffer allocation fails
*/
if (ibptr == NULL)
return -1; /* unsuccessfull */
if ((ibptr->data = (short *) malloc(dvx[i].dmaSize * sizeof(short))) == NULL)
return(-1);
if (j == 0){
dvx[i].inbuf = ibptr; /* initialize if first */
}
ibptr->wordcnt = 0;
ibptr->link = ibptra; /* LINK TO last buffer */
}
dvx[i].inbuf->link = ibptr; /* close list */
/*
* locate sequence RAM in an unassigned portion of VME A24
*
* Use the A24 bus address since the processor may not have placed
* A24 on a 16 MB boundary
*/
pDvxA16->seq_offst = (int)pDvxA24Bus>>8;
dvx[i].csr_shadow = CSR_M_A24; /* enable sequence RAM (shadow csr) */
pDvxA16->csr = dvx[i].csr_shadow; /* enable sequence RAM */
/*
* record card address and allocate input buffers
*/
dvx[i].pdvx2502 = pDvxA16; /* record card address */
/*
* locate and enable sequence RAM
*/
dvx[i].sr_ptr = pDvxA24; /* record seq RAM address */
/*
* set up interrupt handler
*/
dvx[i].csr_shadow |= (intvec<<8); /* load int vector (shadow csr) */
pDvxA16->csr = dvx[i].csr_shadow; /* load int vector */
dvx[i].int_vector = intvec; /* save interrupt vector # */
status = intConnect(INUM_TO_IVEC(intvec),dvx_int,&dvx[i]);
if (status != OK)
return -2; /* abort if can't connect */
sysIntEnable(DVX_INTLEV); /* enable interrupt level */
/* make sure the DMA chip is fully disabled */
dvx_dma_reset(dvx[i].pdvx2502);
dvx[i].csr_shadow |= CSR_M_INT; /* enable fifo int (shadow csr) */
pDvxA16->csr = dvx[i].csr_shadow; /* enable fifo interrupts */
intvec++; /* advance to next vector # */
/*
* test mux cards and load sequence RAM
*/
muxtst(i); /* test mux cards */
sramld(i); /* load scan program */
dvx[i].csr_shadow ^= CSR_M_INT; /* disable fifo int (shadow csr) */
pDvxA16->csr = dvx[i].csr_shadow; /* disable fifo interrupts */
/*
* initialize DMA
*/
dvx_dma_init(&dvx[i]); /* initialize DMA controller */
/*
* set scan rate and enable external start
*/
pDvxA16->samp_rate = DVX_DRATE; /* scan rate of 184 KHz */
dvx[i].csr_shadow |= CSR_M_ESTART; /* enable ext start (shadow csr) */
pDvxA16->csr = dvx[i].csr_shadow; /* enable external start */
dvx[i].mode = RUN_MODE; /* ready to aquire data */
#ifndef EPICS_V2
scanIoInit(dvx[i].pioscanpvt);
#endif
}
return 0; /* return 0 to database */
}
/*
* muxtst
*
* test multiplexer cards
* I suspect this test does nothing more than light the pass LED on all
* the 2601 multiplexer cards.
*
*/
LOCAL int muxtst(int card)
{
int i;
short *ramptr;
/*
* inhibit sys fail and load test setup parameters
*/
dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_SYSFINH;
ramptr = dvx[card].sr_ptr; /* pointer to sequence RAM */
dvx[card].pdvx2502->thresh = TST_THRESH; /* load test threshold */
dvx[card].pdvx2502->samp_rate = TST_RATE; /* test sample rate */
/*
* load test program into sequence RAM
*/
for (i = 0; i < MAX_PORTS; i++)
{
*ramptr++ = GAIN_CHANNEL; /* first sequence RAM value */
*ramptr++ = ADVANCE_HOLD | i; /* mux card select */
}
*ramptr++ = RESTART; /* end of scan */
*ramptr = RESTART;
/*
* run test and restore csr
*/
dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_MXTST;
taskDelay(sysClkRateGet()); /* let test run */
dvx[card].pdvx2502->csr = dvx[card].csr_shadow;
dvx[card].pdvx2502->thresh = 0; /* restore threshold */
}
/*
* dvx_program() is used to define what ports on what boards to scan
* when taking a sample. Each time a start pulse is supplied to the DVX
* board, it will read from the ports in the specified by the user. When
* dmaSize samples have been taken (possibly after many start pulses) the DMA
* controller will terminate transferring data and generate a completion event.
* After this event, the whole process starts over again with the next start
* pulse.
*
* The dvx_program() parms are simply a bit mask of what ports to read from
* each board. There may be up to 8 boards on one DVX master. The bit masks
* are 32 bit unsigned numbers that should be assigned during the startup
* script when booting the IOC.
*
* To program a DVX card (card 0) to read all ports from 2 S/H muxes (boards 2
* and 6) and 1 mux (board 1). You may do the following.
*
* dvx_program(0, 2, 0x0000ffff, 0) -- 16 ports from board 2
* dvx_program(0, 6, 0x0000ffff, 0) -- 16 ports from board 6
* dvx_program(0, 1, 0xffffffff, 64) -- 32 ports from board 1
*
* The 64 on the last line specified that we want to read 64 samples before the
* DMA is considered complete. The last dvx_program() value for the dma size
* is the only one that is used, all previous are discarded.
*
* If so desired, the above example could have used 128 for the DMA size value.
* in this case, the dvx 'system' would not consider the sample complete until
* all the ports were read twice.
*
* NOTE: Each card has its own notion of DMA size. Each card also has its
* own notion of default values. If no programming is done for a
* specific card number, its default values will be used.
*
* The ports are read from the lowest board number and lowest port number first.
*/
int
dvx_program(int card, int board, unsigned long mask, int dmaSize)
{
int i;
unsigned long maskCheck;
int numSamp;
static int firstTime = 1;
if (dvxOnline)
{
printf("DVX cards are already on line, no modifications allowed\n");
return(-1);
}
if ((card < 0) || (card > ai_num_cards[DVX2502]))
{
printf("dvx_program(%d, %d, 0x%08.8X): invalid card number specified\n", card, board, mask);
return(1);
}
if ((board < 0) || (board > 7))
{
printf("dvx_program(%d, %d, 0x%08.8X): invalid board number specified\n", card, board, mask);
return(2);
}
if (firstTime)
{ /* Clear out the default port numbers, this is the first dvx_program call */
int i;
firstTime=0;
for (i=0; i<8; i++)
dvx[card].pgmMask[i] = 0;
}
dvx[card].pgmMask[board] = mask;
dvx[card].dmaSize = dmaSize;
return(0);
}
/*
* Allow the user to specify the interrupt level number
*
* It returns the 'old' IRQ level value.
*/
int dvx_setIrqLevel(int level)
{
int i;
if (dvxOnline)
{
printf("DVX card(s) already initialized at level %d, new IRQ level ignored\n", DVX_INTLEV);
return(DVX_INTLEV);
}
i = DVX_INTLEV;
DVX_INTLEV = level;
return(i);
}
/*
* This can be called by a user program to get information about how
* the dvx card is programmed.
*/
int dvx_getProgram(int card, int *dmaSize, int *numChan)
{
*dmaSize = dvx[card].dmaSize;
*numChan = dvx[card].numChan;
if (dvxDebug)
printf("total DMA samples=%d, total number of physical channels=%d\n", *dmaSize, *numChan);
return(-1);
}
/*
* This version of the sequence program loader allows the number of channels
* to be programmable. It is assumed that the programmable constants will
* be fetched from the user.
*
*/
LOCAL
int sramld(int card)
{
short *ramptr;
int i, port, firstPort;
unsigned long mask;
/* load sequence program */
ramptr = dvx[card].sr_ptr; /* point to sequence RAM */
dvx[card].numChan = 0;
for (i=0; i<8; i++)
{
mask = 1;
port = 0;
firstPort = -1;
while(port < 32)
{
if (mask & dvx[card].pgmMask[i])
{ /* I need to read a sample from this port */
if (firstPort == -1)
{ /* save this one for prescan */
firstPort = port;
}
else
{
*ramptr++ = GAIN_CHANNEL | ((port >> 3) & 3);
*ramptr++ = ADVANCE_HOLD | ((port & 0x07) << 3) | i;
dvx[card].numChan++;
if (dvxDebug)
printf("board %d, port %d\n", i, port);
}
}
mask <<= 1;
port++;
}
if (firstPort != -1)
{ /* Put the first port number to read on each board, last in scan list. */
*ramptr++ = GAIN_CHANNEL | ((firstPort >> 3) & 3);
*ramptr++ = ADVANCE_HOLD | ((firstPort & 0x07) << 3) | i;
dvx[card].numChan++;
if (dvxDebug)
printf("board %d, port %d\n", i, firstPort);
}
}
if (dvxDebug)
printf("Total channels read in %d\n", dvx[card].numChan);
#if 0 /* Force the user to reference board 7 on their own */
/* Make the extender system reset by referencing board 7 (No sample taken) */
*ramptr++ = GAIN_CHANNEL | ((0xF0 >> 3) & 3);
*ramptr++ = (((0xF0 & 7) << 3) | ((0xF0 & 0xE0) >> 5));
#endif
*ramptr++ = 0; /* mark the end of the sequence program */
*ramptr++ = 0;
/* set scan rate and run it once */
dvx[card].pdvx2502->samp_rate = DVX_DRATE;
dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_START;
taskDelay(sysClkRateGet()); /* let scan run */
dvx[card].pdvx2502->csr = dvx[card].csr_shadow; /* restore csr */
}
/*
* dvx_driver
*
* interface to analog input buffer
*
*/
int dvx_driver(
int card,
int chan,
short *pval
)
{
short ival;
if ((card >= ai_num_cards[DVX2502]) || (card < 0)) /* make sure hardware exists */
return -1;
else if (dvx[card].pdvx2502 == NULL)
return -2;
else if (chan > dvx[card].dmaSize)
return -2;
*pval = dvx[card].inbuf->data[chan];
return 0;
}
/*
* dvxReadWf
*
* Allows a waveform record to read all samples from the dvx buffer as a
* waveform.
*/
int dvxReadWf(int card, int start, int num, short *pwf, unsigned long *numRead)
{
int dataIndex;
if(dvxDebug)
printf("dvxReadWf(%d, %d, %d, 0x%08.8X, 0x%08.8X)\n", card, start, num, pwf, numRead);
*numRead = 0; /* in case we have an error condition */
/* make sure hardware exists */
if ((card >= ai_num_cards[DVX2502]) || (card < 0))
return(-1);
else if ((dvx[card].pdvx2502 == NULL)||(start > dvx[card].dmaSize)||
(start < 0)||(num < 1))
return(-2);
/* if user asked for too many, chop off the length to that available */
if (start+num > dvx[card].dmaSize)
num -= (start+num) - dvx[card].dmaSize;
dataIndex = start+num;
*numRead = num;
if (dvxDebug)
printf("dvxReadWf(): Actual elements read: %d\n", num);
while(num)
{
num--;
dataIndex--;
pwf[num] = dvx[card].inbuf->data[dataIndex];
}
return(0);
}
/*
* dvx_dread
*
* stand alone interface to dvx_driver
*
*/
int dvx_dread(int card,int chan)
{
short stat;
short unsigned data;
float volts;
stat = dvx_driver(card,chan,(short *)&data);
volts = data * 10./32767. - 10;
printf("channel # %d\tdata = %x\tvolts = %f\n"
,chan,data,volts);
}
/*
* dvx_dump
*
* dump RAM buffer
*
*/
int dvx_dump(int card,int firstchan,int lastchan)
{
int i, port, ix, printing, tmp;
short unsigned data;
unsigned long mask;
float volts;
printf("Entering dvx_dump with card = %d,firstchan = %d,lastchan = %d\n",card,firstchan,
lastchan);
if ((card >= ai_num_cards[DVX2502]) || (card < 0))
return -1;
else if (dvx[card].pdvx2502 == 0)
return -2;
printf("buffer address = %x word count = %d interrupt count = %d\n",
dvx[card].inbuf,dvx[card].inbuf->wordcnt,dvx[card].intcnt);
if (dvx[card].pdvx2502->csr & 0x2)
printf("fifo status = not empty,");
else
printf("fifo status = empty,");
printf(" current input channel = %x\n",
(dvx[card].pdvx2502->csr & 0x3fc0)>>6);
ix = 0;
printing= 1;
while ((ix < dvx[card].dmaSize) && (ix < lastchan) && printing)
{
printing = 0;
for (i=0; i<8; i++)
{
mask = 1;
port = 0;
while(port < 32)
{
if (mask & dvx[card].pgmMask[i])
{
if (ix >= firstchan)
{
tmp = dvx[card].inbuf->data[ix];
tmp &= 0x0000ffff;
volts = tmp * 10./32767.;
printf("signal %2d, board %d, port %2d, data 0x%04.4X, voltage %f\n", ix, i, port, tmp, volts);
}
ix++;
printing = 1;
}
mask <<= 1;
port++;
}
}
}
printf("end of list\n");
return 0;
}
#ifndef EPICS_V2
dvx_getioscanpvt(int card, IOSCANPVT *scanpvt)
{
if ((card >= ai_num_cards[DVX2502]) || (card < 0))return(0);
if (dvx[card].pdvx2502 == 0) return(0);
*scanpvt = *(dvx[card].pioscanpvt);
return(0);
}
#endif
/*
*
* dvx_io_report
*
*
*/
long dvx_io_report(int level)
{
short int i;
unsigned short card_id;
for (i = 0; i < ai_num_cards[DVX2502]; i++){
if (!dvx[i].pdvx2502)
continue;
/* If detected card is a 2502 print out its number. */
printf("AI: DVX2505:\tcard %d\n",i);
if(level > 0 ){
int firstchan;
int lastchan;
printf("Enter number of the first channel you wish to read:\n");
scanf("%d",&firstchan);
printf("First channel is %d\n",firstchan);
printf("Enter number of the last channel you wish to read:\n");
scanf("%d",&lastchan);
printf("Last channel is %d\n",lastchan);
dvx_dump(i,firstchan,lastchan);
}
}
return OK;
}
/*
* dvx_fempty
*
* empty fifo
*
*/
LOCAL int dvx_fempty(int card)
{
int i, junk;
if ((card>= ai_num_cards[DVX2502]) || (card < 0))
return -1;
else if (dvx[card].pdvx2502 == 0)
return -2;
for (i = 0; dvx[card].pdvx2502->csr & 0x2; i++)
junk = dvx[card].pdvx2502->fifo;
printf("%d words read from fifo\n",i);
return 0;
}
LOCAL dvx_dma_reset(struct dvx_2502 *dev)
{
dev->dma_point = DMA_CSR;
dev->dma_data = CMR_RESET; /* reset the thing */
return(0);
}
/*
* dvx_dma_init
*
* am9516 DMA controller initialization
*
* local to A24 bus addr conversions below are necessary on processors
* that dont place the local base for A24 on an even 16 MB boundary
*/
LOCAL dvx_dma_init(struct dvx_rec *ptr)
{
int i, j;
int status;
short *cptr, *cptra, *cptr0, *pext;
short *BusPtr;
struct dvx_2502 *dev;
struct dvx_inbuf *bpnt;
dev = ptr->pdvx2502; /* point to hardware */
dvx_dma_reset(dev); /* reset the DMA chip */
/* build chain table */
if ((cptr = cptr0 = (short *)malloc(DVX_CTBL)) == NULL)
return -1;
dev->dma_point = DMA_MMR; /* enable chip */
dev->dma_data = MMR_ENABLE;
/*
* The 2502 uses A24 priv data VME addr mods
* for its DMAchain operations
*/
status = sysLocalToBusAdrs(
VME_AM_STD_SUP_DATA,
cptr,
&BusPtr);
if(status < 0){
logMsg( "%s:Local chain addr 0x%X does not map to A24 bus addr\n",
__FILE__,
cptr);
return -1;
}
dev->dma_point = DMA_CARH; /* load init chain address */
dev->dma_data = (int) BusPtr>>8 & 0xff00;
dev->dma_point = DMA_CARL;
dev->dma_data = (int) BusPtr & 0xffff;
for ( i = 0, bpnt = ptr->inbuf->link;
i < DVX_NBUF;
i++, cptr = cptra, bpnt = bpnt->link)
{ /* create chain for each input buffer */
if ((i + 1) == DVX_NBUF)
cptra = cptr0; /* close list */
else
if ((cptra = (short *)malloc(DVX_CTBL)) == NULL)
return -1; /* allocate next chain */
/* Set the reload word */
*cptr++ = R_CAA | R_CAB | R_COC | R_CMR | R_CAR; /* load mask */
/*
*
* source options:
* 1) data (not chain) operation
* 2) hold (dont auto incr or decr) the src address
*
* The src addr here is ignored by the dvx2502 hardware
* and is therefore set to zero. The source is always
* the dvx 2502 fifo.
*/
*cptr++ = 0xd0;
*cptr++ = 0; /* address reg A (src) */
/*
* The 2502 uses A24 non-priv data VME addr mods
* for its DMA data transfers
*/
status = sysLocalToBusAdrs(
VME_AM_STD_USR_DATA,
bpnt->data,
&BusPtr);
if(status < 0){
logMsg( "%s: Local dest addr 0x%X does not map to A24 addr\n",
__FILE__,
bpnt->data);
return -1;
}
*cptr++ = (((int)BusPtr>>8) & 0xff00) | 0xc0;
*cptr++ = (int) BusPtr & 0xffff; /* address reg B (dest) */
*cptr++ = ptr->dmaSize; /* operation count */
*cptr++ = 0x4;
*cptr++ = 0x0252; /* dma mode control */
/*
* The 2502 uses A24 priv data VME addr mods
* for its DMA chain operations
*/
status = sysLocalToBusAdrs(
VME_AM_STD_SUP_DATA,
cptra,
&BusPtr);
if(status < 0){
logMsg( "%s:Local addr 0x%X does not map to A24 addr\n",
__FILE__,
cptra);
return -1;
}
*cptr++ = (int)BusPtr>>8 & 0xff00;
*cptr = (int)BusPtr& 0xffff; /* next chain address */
}
/* enable DMA opeations */
dev->dma_point = DMA_CSR;
dev->dma_data = CMR_SC | M_CIE | M_CH2; /* enable int channel #2 */
dev->dma_data = CMR_START | M_CH2; /* start channel #2 */
return 0;
}
/*
* dvx_dma_stat
*
* reads status of dma channel
*
*/
int dvx_dma_stat(int card, int chan)
{
struct dvx_2502 *ptr;
short unsigned temp;
if ((card < 0) || (card > ai_num_cards[DVX2502]))
return -1;
else if (dvx[card].pdvx2502 == 0)
return -2;
else
{
ptr = dvx[card].pdvx2502;
temp = (chan & 0x1)<<1;
ptr->dma_point = DMA_CSR | temp;
printf("dma status = %x\n",ptr->dma_data);
ptr->dma_point = DMA_MMR;
printf("master mode register = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARH | temp;
printf("chain address high = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARL | temp;
printf("chain address low = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARAH | temp;
printf("current address register A high = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARAL | temp;
printf("current address register A low = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARBH | temp;
printf("current address register B high = %x\n",ptr->dma_data);
ptr->dma_point = DMA_CARBL | temp;
printf("current address register B low = %x\n",ptr->dma_data);
ptr->dma_point = DMA_BARAH | temp;
printf("base address register A high = %x\n",ptr->dma_data);
ptr->dma_point = DMA_BARAL | temp;
printf("base address register A low = %x\n",ptr->dma_data);
ptr->dma_point = DMA_BARBH | temp;
printf("base address register B high = %x\n",ptr->dma_data);
ptr->dma_point = DMA_BARBL | temp;
printf("base address register B low = %x\n",ptr->dma_data);
ptr->dma_point = DMA_COC | temp;
printf("current operation count = %x\n",ptr->dma_data);
ptr->dma_point = DMA_BOC | temp;
printf("base operation count = %x\n",ptr->dma_data);
}
return 0;
}
/*
*
* dvx_reset
* joh 072591
*
* (JRW this should run thru the chain ok init'd boards, not do a probe)
*
*/
LOCAL void
dvx_reset(void)
{
struct dvx_2502 *pDvxA16;
unsigned short card_id;
int i;
int status;
int card_found = FALSE;
status = sysBusToLocalAdrs(
VME_AM_SUP_SHORT_IO,
ai_addrs[DVX2502],
&pDvxA16);
if (status != OK){
logMsg( "%s: A16 base addr problems DVX 2502\n",
__FILE__);
return;
}
/*
* search for cards
*/
for (i = 0; i < ai_num_cards[DVX2502]; i++, pDvxA16++){
status = vxMemProbe (
&pDvxA16->dev_id,
READ,
sizeof(card_id),
&card_id);
if (status != OK)
continue;
/*
* see if detected card is a 2502
* and reset if so
*/
if (card_id == DVX_ID){
/* reset the DMA controller */
dvx_dma_reset(pDvxA16);
pDvxA16->csr = CSR_RESET;
card_found = TRUE;
}
}
/*
* wait long enough for the current DMA to end
*
* 1 sec
*/
if(card_found){
printf("Waiting for DVX 2502 DMA to complete...");
taskDelay(sysClkRateGet());
printf("done\n");
}
}