/* Copyright 1986 by the Massachusetts Institute of Technology */

/* Device driver for the ACC DMA 1822 interface.
 */

#include	<types.h>
#include	<sys.h>
#include	<sysext.h>
#include	<gw/src/status.h>


/* Device registers */
struct dma_rreg {
    unss r_csr;			/* control/status register */
    unss r_dbr;			/* data buffer register */
    unss r_car;			/* current address register */
    unss r_wcr;			/* word count register (neg cnt to 0) */
};
struct dma_treg {
    unss t_csr;			/* control/status register */
    unss t_dbr;			/* data buffer register */
    unss t_car;			/* current address register */
    unss t_wcr;			/* word count register (neg cnt to 0) */
};

/* Receive CSR definitions */
#define GO   01			/* 1=enables DMA transfer */
#define RST  02			/* reset, 1=clears dev (drops host rdy line) */
#define HRLY 04			/* host relay control, 1=raise host rdy line */
#define EN   010		/* Receive enable, 1=normal data transfer */
#define IE   0100		/* Interrupt enable */
#define RDY  0200		/* Receive ready, 1=recv rdy for GO cmd */
#define RBF  0400		/* Receive buffer full */
#define RYER 01000		/* Relay error tst, 1=host or IMP rdy low */
#define IRYO 02000		/* IMP relay open, 1=IMP rdy low */
#define HRYO 04000		/* Host relay open, 1=host rdy low */
#define REOM 020000		/* Recv end of msg, 1=recvd last bit */
#define NXM  040000		/* Recv memory timeout, 1=nonexistant memory */
#define ERR  0100000		/* Recv error, 1=ERR (reflects timeout err) */

/* Transmit CSR definitions (commented definitions are the same as for
   receive) */
/* #define GO 01		/* 1=enables DMA transfer */
/* #define RST 02		/* reset, 1=clears dev (drops host rdy line) */
#define LBIT   04		/* Last bit, 1=tx to send LB when WD cnt=0 */
#define BBAK   010		/* Bus back, 1=controller loopback */
/* #define IE  0100		/* Transmit interupt enable */
/* #define RDY 0200		/* Transmit ready, 1=TX rdy for GO cmd */
#define TBE    0400		/* Tramsmit buffer empty */
/* define RYER 01000		/* Relay error, 1=host or IMP rdy low */
/* #define NXM 040000		/* Tran memory timeout, 1=nonexistant memory */
/* #define ERR 0100000		/* Trans error, 1=ERR (reflects timeout err) */

/* Error codes */
#define ER_OFLN 1		/* Offline */
#define ER_FLSH 2		/* Flush packet */
#define ER_OVFL 3		/* Overflow */

/* Uses D_DV1 to flag truncated packets. */
#define D_TRUNC D_DV1


/*
 *	Definitions and macros for dealing with the Q-Bus map
 * registers.  The ACC DMA card can only deal with 16 bit qbus
 * addresses.  Even if it could do 18 bit addresses, the buffers now
 * go beyond 256k.  So the Q-Bus map is needed to make it work.  This
 * code is wired such that only one DMA interface can be configured
 * and the packet buffers can be no bigger than 3.5k.  Eventually, the
 * code and macros to deal with the Q-Bus map should be generalized
 * and a device which needs to make use of the Q-Bus map would
 * allocate the appropriate number of mapping registers for the size
 * of packet buffers being used.  And by treating the map registers as
 * a resource which is allocated, then more than one device which
 * needed the Q-Bus map would ba able to work in the same machine.
 */
#define QMR ((unsl *)0x20088000) /* Array of Q-Bus mapping registers */
#define QMR_VALID 0x80000000	/* Valid bit in Q-Bus mapping register */
#define PGSZ 512		/* Page size */
#define LOG_PGSZ 9		/* log2(PGSZ) */

/* Set the Q-Bus map for bus address qaddr to memory address maddr. */
#define set_qmap(qaddr, maddr) \
  QMR[((unsl)(qaddr))>>LOG_PGSZ] = (((unsl)(maddr))>>LOG_PGSZ)|QMR_VALID;

#define RX_QBASE 0		/* Somewhat arbitrary values which should */
#define TX_QBASE 8192		/* really be assigned by routines which deal */
				/* with managing the Q-Bus map registers. */

/* DMA device initialization. */
dma_up(devp)
reg dct *devp;
{
    reg struct dma_rreg *dreg;

    if (!(devp->d_lnk->d_flg & D_INI)) {
	dreg = (struct dma_rreg *)devp->d_csr;
	dreg->r_csr = RST;
    }
    devp->d_flg = D_INI;
}

/* Input transfer initialization routine */
dmain(devp)
reg dct *devp;
{
    reg unss csr;
    reg struct dma_rreg *dreg;

    dreg = (struct dma_rreg *)devp->d_csr;
    if (dreg->r_csr & HRYO)
      dreg->r_csr |= HRLY | EN;	/* Raise host ready line */
    /* Set up Q-Bus mapping registers */
    set_qmap(RX_QBASE, devp->d_addr);
    set_qmap(RX_QBASE + 1*PGSZ, devp->d_addr + 1*PGSZ);
    set_qmap(RX_QBASE + 2*PGSZ, devp->d_addr + 2*PGSZ);
    set_qmap(RX_QBASE + 3*PGSZ, devp->d_addr + 3*PGSZ);
    set_qmap(RX_QBASE + 4*PGSZ, devp->d_addr + 4*PGSZ);
    set_qmap(RX_QBASE + 5*PGSZ, devp->d_addr + 5*PGSZ);
    set_qmap(RX_QBASE + 6*PGSZ, devp->d_addr + 6*PGSZ);
    set_qmap(RX_QBASE + 7*PGSZ, devp->d_addr + 7*PGSZ);

    dreg->r_wcr = -(devp->d_breq >> 1);	/* assume even count */
    dreg->r_car = ((unsl)devp->d_addr & (PGSZ-1)) + RX_QBASE;
    dreg->r_csr = GO | HRLY | EN | IE;
}

/* Output transfer initialization routine */
dmaot(devp)
reg dct *devp;
{
    reg struct dma_rreg *rdreg;
    reg struct dma_treg *dreg;

    devp->d_qhd->i_usr5 = systick;

    dreg = (struct dma_treg *)devp->d_csr;
    if ((dreg->t_csr & RDY) == 0)
      bughalt("ARPA xmt nt rdy");

    rdreg = (struct dma_rreg *)(devp->d_lnk->d_csr);
    if (rdreg->r_csr & HRYO) {
	rdreg->r_csr |= HRLY | EN;	/* Raise host ready line */
	rdreg->r_csr |= IE;	/* Enable receive interrupts */
    }
    /* Set up Q-Bus mapping registers */
    set_qmap(TX_QBASE, devp->d_addr);
    set_qmap(TX_QBASE + 1*PGSZ, devp->d_addr + 1*PGSZ);
    set_qmap(TX_QBASE + 2*PGSZ, devp->d_addr + 2*PGSZ);
    set_qmap(TX_QBASE + 3*PGSZ, devp->d_addr + 3*PGSZ);
    set_qmap(TX_QBASE + 4*PGSZ, devp->d_addr + 4*PGSZ);
    set_qmap(TX_QBASE + 5*PGSZ, devp->d_addr + 5*PGSZ);
    set_qmap(TX_QBASE + 6*PGSZ, devp->d_addr + 6*PGSZ);
    set_qmap(TX_QBASE + 7*PGSZ, devp->d_addr + 7*PGSZ);

    dreg->t_wcr = -(devp->d_breq >> 1);	/* assume even count */
    dreg->t_car = ((unsl)devp->d_addr & (PGSZ-1)) + TX_QBASE;
    dreg->t_csr = GO | LBIT | IE;

    if (rdreg->r_csr & IRYO)	/* Check IMP rdy line status */
      dreg->t_csr |= RDY;	/* stop DMA */
}

/* Receive interrupt handler */
dmaii(devp)
reg dct *devp;
{
    reg unss csr;
    struct dma_rreg *dreg;
    struct dma_treg *tdreg;
    reg iorb *iob;
    int err;

    err = 0;
    dreg = (struct dma_rreg *)devp->d_csr;
    csr = dreg->r_csr;
    if (csr & RYER) {		/* Ready error */
	tdreg = (struct dma_treg *)(devp->d_lnk->d_csr);
	dreg->r_csr |= RDY;	/* Stop receive DMA */
	tdreg->t_csr |= RDY;	/* Stop transmit DMA */
	err = ER_OFLN;		/* Offline */
    }
    else if (!(csr & REOM)) {	/* Not end-of-message */
	dreg->r_csr |= RDY;
	if (devp->d_flg & D_TRUNC) { /* If not first part of fragment */
	    err = ER_FLSH;	/* Flush packet */
	}
	else {
	    err = ER_OVFL;	/* Overflow */
	    devp->d_flg |= D_TRUNC; /* Remember, next packet is also bogus. */
	}
    }
    else if (devp->d_flg & D_TRUNC) { /* Last frag of truncated packet */
	err = ER_FLSH;		/* Flush packet */
	devp->d_flg &= ~D_TRUNC;
    }

    if ((iob = devp->d_qhd) == 0)
      return;
    iob->i_stat |= err | I_DONE;
    iob->i_bxfr = (dreg->r_wcr << 1) + devp->d_breq;
    iocmr(devp);
    return;
}

/* Transmit interrupt handler */
dmaoi(devp)
reg dct *devp;
{
    reg struct dma_treg *dreg;
    reg iorb *iob;
    unss csr;

    dreg = (struct dma_treg *)devp->d_csr;
    iob = devp->d_qhd;
    iob->i_bxfr = (dreg->t_wcr << 1) + devp->d_breq;
    csr = dreg->t_csr;
    if (csr & RYER)
      iob->i_stat |= I_ERR;
    if (csr & NXM)
      bughalt("NXM in ARPA DMA");
    iob->i_stat |= I_DONE;
    iob->i_usr5 = systick - iob->i_usr5;
    iocmr(devp);
}

/* Raise host ready line */
dmarr(devp)
reg dct *devp;			/* input DCT */
{
    reg struct dma_rreg *dreg;

    dreg = (struct dma_rreg *)devp->d_csr;
    dreg->r_csr |= HRLY;
    dreg->r_csr |= IE;
}

/* Drop host ready line */
dmadr(devp)
reg dct *devp;			/* input DCT */
{
    reg struct dma_rreg *dreg;

    dreg = (struct dma_rreg *)devp->d_csr;
    dreg->r_csr &= ~HRLY;
    dreg->r_csr |= IE | EN;
}
