/*
 *	3COM Etherlink II driver
 *
 *	The Etherlink II controller consists of two programable
 *	components: 3Com's gate array and National's DP8390
 *	Network Interface Controller.
 */

#include "config.h"
#include "packet.h"
#include "header.h"

#if (BOARD == CG16)
#   define IOBASE	0xc00300 	/* I/O base */
#endif
#if (BOARD == SHARK_CCA)
#   define IOBASE	0xfc0300 	/* I/O base */
#endif
#if (BOARD == LX32)
#   define IOBASE	0xA2000300  	/* I/O base */
#endif
#if (BOARD == BARACUDA_CCA)
#   define IOBASE	0xF00C0300
#endif


#define	MAXPACK	1522		/* max packet size */
#define	MAXRETRY 1		/* max transmission retries */

/*
 * the structure is defined using the page 0 names,
 * all other registers are #defined somewhere else
 */

struct etherlink2 {
	/*
	 * NIC registers
	 */
	unsigned char	cr;		/* command register */
	unsigned char	pstart;		/* page start */
	unsigned char	pstop;		/* page stop */
	unsigned char	bnry;		/* boundary */
	unsigned char	tpsr;		/* transmit page start */
	unsigned char	tbcr0;		/* xmit byte count 0 */
	unsigned char    tbcr1;		/* xmit byte count 1 */
	unsigned char	isr;		/* interrupt status */
	unsigned char	rsar0;		/* remote start addr 0 */
	unsigned char	rsar1;          /* remote start addr 1 */
	unsigned char	rbcr0;		/* remote byte count 0 */
	unsigned char    rbcr1;		/* remote byte count 1 */
	unsigned char	rcr;		/* receive config */
	unsigned char	tcr;		/* xmit config */
	unsigned char	dcr;		/* data config */
	unsigned char	imr;		/* interrupt mask */
	unsigned char	dummy[1008];	/* the gap between nic and */
				/* the gate array registers */
	/*
	 * Gate array registers
	 */
	unsigned char	pstr;		/* page start (gate array) */
	unsigned char	pspr;		/* page stop */
	unsigned char	dqtr;		/* drq timer */
	unsigned char	bfcr;		/* base config register */
	unsigned char	pcfr;		/* eprom config reg */
	unsigned char	gacfr;		/* gate array config */
	unsigned char	ctrl;		/* control register */
	unsigned char	streg;		/* status register */
	unsigned char	idcfr;		/* interrupt/dma */
	unsigned char	damsb;		/* dma address - msb */
	unsigned char	dalsb;		/* dma address - lsb */
	unsigned char	vptr2;		/* vector pointer reg 2 */
	unsigned char    vptr1;		/* vector pointer reg 1 */
	unsigned char	vptr0;		/* vector pointer reg 0 */
	union {
		struct	{
			unsigned char	c_rflsb; /* fifo lsb */
			unsigned char	c_rfmsb; /* fifo msb */
		} fifo_b;
		unsigned short s_rflsb;
	} fifo_u;
};

#define romeaddr cr
#define par0	pstart
#define curr	isr
#define	w_rflsb fifo_u.s_rflsb
#define	b_rflsb fifo_u.fifo_b.c_rflsb

/*
 * init_3com - controller initialization
 * 
 * ttype specifies transciever type to use:
 * 0 internal, not 0 external
 */

init_3com(ttype)
int ttype;
{
	 volatile struct etherlink2 * volatile ep = (struct etherlink2 *)IOBASE;
	ep->ctrl = 0x3;
	ep->ctrl = 0x2;
	ep->ctrl = 0x6;
	bcopy(&ep->romeaddr, &evars.eaddr[0], 6);

	if (ttype != 0)
		ep->ctrl = 0;	/* external transceiver */
	else
		ep->ctrl = 0x2;	/* internal transceiver */
	ep->pstr = 0x26;
	ep->pspr = 0x40;
	ep->idcfr = 0x1;
	ep->dqtr = 0x8;
	ep->damsb = 0x20;
	ep->dalsb = 0;
	return(init_nic());
}

/*
 * init_nic - init the Network Interface Controller
 */

init_nic()
{
	 volatile  struct etherlink2 * volatile ep = (struct etherlink2 *)IOBASE;

	ep->cr = 0x21;
	while ((ep->isr & 0x80) == 0)
		;
	ep->dcr = 0x48;
	ep->rbcr0 = 0;
	ep->rbcr1 = 0;
	ep->tpsr = 0x20;
	ep->rcr = 0x20;
	ep->tcr = 0x2;
	ep->pstart = 0x26;
	ep->bnry = 0x26;
	ep->pstop = 0x40;
	ep->isr = 0xff;
	ep->imr = 0;
	ep->cr = 0x61;
	bcopy(&evars.eaddr[0], &ep->par0, 6);
	ep->curr = 0x27;
	evars.nxtpkt = 0x27;
	ep->cr = 0x21;
	ep->cr = 0x22;
	ep->rcr = 0;
	ep->tcr = 0;
	return 1;
}

/*
 * eread - read a packet from the net
 */

eread()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	int	i;

	if ((ep->isr & 0x10) != 0) {
		overflow();
		return 1;
	}
	ep->cr = 0x62;
	i = ep->curr;
	ep->cr = 0x22;
	if (i == evars.nxtpkt)
		return 1; 	/* no packet */
	return(get_next());
}

/*
 * overflow - overflow handler
 */

static overflow()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	int	i;

	ep->cr = 0x21;
	ep->rbcr0 = 0;
	ep->rbcr1 = 0;
	while ((ep->isr & 0x80) == 0)
		;
	ep->tcr = 0x2;
	ep->cr = 0x22;
	i = get_next();
	ep->isr |= 0x10;
	ep->rcr = 0;
	ep->tcr = 0;
	return i;
}

/*
 * get_next - get the next packet from NIC's buffer ring
 */

get_next()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	int	size,i;
	unsigned short *sp;
	unsigned char *cp;

	ep->isr |= 1;
	size = head_check();
	if (size == 0)
		return 1;	/* no packet */
	ep->ctrl &= ~ 0x80;
	while ((ep->streg & 0x8) != 0)
		;
	ep->damsb = (char)evars.nxtpkt;
	ep->dalsb = 0;
	sp = (unsigned short *)&evars.packet;
	ep->ctrl &= ~ 0x40;
	ep->ctrl |= 0x80;
	for (i = 0; i < size; i+=8) {
		while ((ep->streg & 0x80) == 0)
			;
		/* read dma burst */
		*sp++ = ep->w_rflsb;
		*sp++ = ep->w_rflsb;
		*sp++ = ep->w_rflsb;
		*sp++ = ep->w_rflsb;
	}
	cp = (unsigned char *)sp;
	for (i = size & 0x7;  i > 0; i--) {
		while ((ep->streg & 0x80) == 0)
			;
		*cp++ = ep->b_rflsb;
	}
	ep->ctrl &= ~ 0x80;
	evars.nxtpkt = evars.packet.ep_hdr.npp;
	ep->bnry = evars.packet.ep_hdr.npp - 1;
	if (ep->bnry < 0x26)
		ep->bnry = 0x3f;
	return 0;	/* success */
}

head_check()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	unsigned short *sp;

	ep->ctrl &= ~ 0x80;
	while ((ep->streg & 0x8) != 0)
		;
	ep->damsb = evars.nxtpkt;
	ep->dalsb = 0;
	ep->ctrl &= ~ 0x40;
	ep->ctrl |= 0x80;
	while ((ep->streg & 0x80) == 0)
		;
	sp = (unsigned short *)&evars.packet.ep_hdr;
	*sp++ = ep->w_rflsb;
	*sp = ep->w_rflsb;
	ep->ctrl &= ~ 0x80;
	while ((ep->streg & 0x8) != 0)
		;
	if (evars.packet.ep_hdr.rstatus & 0x01) {
		if (evars.packet.ep_hdr.rbc <= MAXPACK) {
			return evars.packet.ep_hdr.rbc;
		}
		evars.nxtpkt = evars.packet.ep_hdr.npp;
		ep->bnry = evars.nxtpkt - 1;
		if (ep->bnry < 0x26)
			ep->bnry = 0x3f;
	} 
	init_nic();
	return 0;
}

/*
 * ewrite - transmit packet, the packet data is in evars.packet
 */

ewrite()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	register unsigned short	*sp;
	register unsigned char *cp;
	int	size, i, j;

	evars.retry = 0;
	bcopy(&evars.eaddr[0], evars.packet.ep_hdr.e_src, 6);
	ep->ctrl &= ~ 0x80;
	while ((ep->streg & 0x8) != 0)
		;
	ep->damsb = 0x20;
	ep->dalsb = 0;
	sp = (unsigned short *)&evars.packet.ep_hdr.e_dest[0];
	size = evars.packet.ep_hdr.rbc;
	ep->ctrl |= 0xc0;
	/*
	ep->ctrl |= 0x40;
	ep->ctrl |= 0x80;
	*/
	for (i = 0; i < size; i += 8) {
		while ((ep->streg & 0x80) == 0)
			;
		ep->w_rflsb = *sp++;
		ep->w_rflsb = *sp++;
		ep->w_rflsb = *sp++;
		ep->w_rflsb = *sp++;
	}
	cp = (unsigned char *)sp;
	for (i = size & 0x7;  i > 0; i--) {
		while ((ep->streg & 0x80) == 0)
			;
		ep->b_rflsb = *cp++;
	}
	ep->ctrl &= ~ 0x80;
	while ((ep->streg & 0x8) != 0)
		;
	again:
	ep->tbcr0 = evars.packet.ep_hdr.rbc & 0xff;
	ep->tbcr1 = (evars.packet.ep_hdr.rbc >> 8) & 0xff;
	ep->tcr = 0;
	ep->cr = 0x26;
	for (i = 0; i < 60000; i++) {
		if ((ep->isr & 0x2) != 0)
			break;
		if ((ep->isr & 0x8) != 0) {
			/* transmission error, retry */
			ep->isr |= 0x8;
			if (evars.retry++ >= MAXRETRY) {
				return 1;
			}
			goto again;
		}
	}
	ep->isr |= 0x2;
	return 0;
}

overwrite()
{
	 volatile struct etherlink2  * volatile ep = (struct etherlink2 *)IOBASE;
	
	if ((ep->isr & 0x10) == 0)
		return 0;
	return 1;
}

