/**************************************************************************
ETHERBOOT -  BOOTP/TFTP Bootstrap Program

Author: Martin Renters
  Date: May/94

 This code is based heavily on David Greenman's if_ed.c driver

 Copyright (C) 1993-1994, David Greenman, Martin Renters.
  This software may be used, modified, copied, distributed, and sold, in
  both source and binary form provided that the above copyright and these
  terms are retained. Under no circumstances are the authors responsible for
  the proper functioning of this software, nor do the authors assume any
  responsibility for damages incurred with its use.

3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
  parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)

**************************************************************************/

#include "etherboot.h"
#include "nic.h"
#include "ns8390.h"
#ifdef	INCLUDE_NS8390
#include "pci.h"
#endif
#include "cards.h"

static unsigned char	eth_vendor, eth_flags, eth_laar;
static unsigned short	eth_nic_base, eth_asic_base;
static unsigned char	eth_memsize, eth_rx_start, eth_tx_start;
static Address		eth_bmem, eth_rmem;
static unsigned char	eth_drain_receiver;

#ifdef	INCLUDE_WD
static struct wd_board {
	const char *name;
	char id;
	char flags;
	char memsize;
} wd_boards[] = {
	{"WD8003S",	TYPE_WD8003S,	0,			MEM_8192},
	{"WD8003E",	TYPE_WD8003E,	0,			MEM_8192},
	{"WD8013EBT",	TYPE_WD8013EBT,	FLAG_16BIT,		MEM_16384},
	{"WD8003W",	TYPE_WD8003W,	0,			MEM_8192},
	{"WD8003EB",	TYPE_WD8003EB,	0,			MEM_8192},
	{"WD8013W",	TYPE_WD8013W,	FLAG_16BIT,		MEM_16384},
	{"WD8003EP/WD8013EP",
			TYPE_WD8013EP,	0,			MEM_8192},
	{"WD8013WC",	TYPE_WD8013WC,	FLAG_16BIT,		MEM_16384},
	{"WD8013EPC",	TYPE_WD8013EPC,	FLAG_16BIT,		MEM_16384},
	{"SMC8216T",	TYPE_SMC8216T,	FLAG_16BIT | FLAG_790,	MEM_16384},
	{"SMC8216C",	TYPE_SMC8216C,	FLAG_16BIT | FLAG_790,	MEM_16384},
	{"SMC8416T",	TYPE_SMC8416T,	FLAG_16BIT | FLAG_790,	MEM_8192},
	{"SMC8416C/BT",	TYPE_SMC8416C,	FLAG_16BIT | FLAG_790,	MEM_8192},
	{"SMC8013EBP",	TYPE_SMC8013EBP,FLAG_16BIT,		MEM_16384},
	{NULL,		0,		0,			0}
};
#endif

#ifdef	INCLUDE_3C503
static unsigned char	t503_output;	/* AUI or internal xcvr (Thinnet) */
#endif

#if	defined(INCLUDE_WD)
#define	eth_probe	wd_probe
#if	defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
#endif
#endif

#if	defined(INCLUDE_3C503)
#define	eth_probe	t503_probe
#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD)
Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
#endif
#endif

#if	defined(INCLUDE_NE)
#define	eth_probe	ne_probe
#if	defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
#endif
#endif

#if	defined(INCLUDE_NS8390)
#define	eth_probe	nepci_probe
#if	defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
#endif
#endif

#if	defined(INCLUDE_3C503)
#define	ASIC_PIO	_3COM_RFMSB
#else
#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
#define	ASIC_PIO	NE_DATA
#endif
#endif

#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM))
/**************************************************************************
ETH_PIO_READ - Read a frame via Programmed I/O
**************************************************************************/
static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt)
{
	if (eth_flags & FLAG_16BIT) { ++cnt; cnt &= ~1; }
	outb(D8390_COMMAND_RD2 |
		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
	outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
	outb(src, eth_nic_base + D8390_P0_RSAR0);
	outb(src>>8, eth_nic_base + D8390_P0_RSAR1);
	outb(D8390_COMMAND_RD0 |
		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);

#ifdef	INCLUDE_3C503
	outb(src & 0xff, eth_asic_base + _3COM_DALSB);
	outb(src >> 8, eth_asic_base + _3COM_DAMSB);
	outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR);
#endif

	if (eth_flags & FLAG_16BIT)
		cnt >>= 1;

	while(cnt--) {
#ifdef	INCLUDE_3C503
		while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
			;
#endif

		if (eth_flags & FLAG_16BIT) {
			*((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO);
			dst += 2;
		}
		else
			*(dst++) = inb(eth_asic_base + ASIC_PIO);
	}

#ifdef	INCLUDE_3C503
	outb(t503_output, eth_asic_base + _3COM_CR);
#endif
}

/**************************************************************************
ETH_PIO_WRITE - Write a frame via Programmed I/O
**************************************************************************/
static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt)
{
#ifdef	COMPEX_RL2000_FIX
	unsigned int x;
#endif	/* COMPEX_RL2000_FIX */
	if (eth_flags & FLAG_16BIT) { ++cnt; cnt &= ~1; }
	outb(D8390_COMMAND_RD2 |
		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
	outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
	outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
	outb(dst, eth_nic_base + D8390_P0_RSAR0);
	outb(dst>>8, eth_nic_base + D8390_P0_RSAR1);
	outb(D8390_COMMAND_RD1 |
		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);

#ifdef	INCLUDE_3C503
	outb(dst & 0xff, eth_asic_base + _3COM_DALSB);
	outb(dst >> 8, eth_asic_base + _3COM_DAMSB);

	outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR);
#endif

	if (eth_flags & FLAG_16BIT)
		cnt >>= 1;

	while(cnt--)
	{
#ifdef	INCLUDE_3C503
		while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
			;
#endif

		if (eth_flags & FLAG_16BIT) {
			outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO);
			src += 2;
		}
		else
			outb(*(src++), eth_asic_base + ASIC_PIO);
	}

#ifdef	INCLUDE_3C503
	outb(t503_output, eth_asic_base + _3COM_CR);
#else
#ifdef	COMPEX_RL2000_FIX
	for (x = 0;
		x < COMPEX_RL2000_TRIES &&
		(inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
		!= D8390_ISR_RDC;
		++x);
	if (x >= COMPEX_RL2000_TRIES)
		printf("Warning: Compex RL2000 aborted wait!\n");
#endif	/* COMPEX_RL2000_FIX */
	while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
		!= D8390_ISR_RDC);
#endif
}
#else
/**************************************************************************
ETH_PIO_READ - Dummy routine when NE2000 not compiled in
**************************************************************************/
static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {}
#endif

/**************************************************************************
NS8390_RESET - Reset adapter
**************************************************************************/
static void ns8390_reset(struct nic *nic)
{
	int i;

	eth_drain_receiver = 0;
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
	if (eth_flags & FLAG_16BIT)
		outb(0x49, eth_nic_base+D8390_P0_DCR);
	else
		outb(0x48, eth_nic_base+D8390_P0_DCR);
	outb(0, eth_nic_base+D8390_P0_RBCR0);
	outb(0, eth_nic_base+D8390_P0_RBCR1);
	outb(0x20, eth_nic_base+D8390_P0_RCR);	/* monitor mode */
	outb(2, eth_nic_base+D8390_P0_TCR);
	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
	outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790) outb(0, eth_nic_base + 0x09);
#endif
	outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
	outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
	outb(0xFF, eth_nic_base+D8390_P0_ISR);
	outb(0, eth_nic_base+D8390_P0_IMR);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS1 |
			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS1 |
			D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
	for (i=0; i<ETH_ALEN; i++)
		outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
	for (i=0; i<ETH_ALEN; i++)
		outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
	outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	outb(0xFF, eth_nic_base+D8390_P0_ISR);
	outb(0, eth_nic_base+D8390_P0_TCR);
	outb(4, eth_nic_base+D8390_P0_RCR);	/* allow broadcast frames */

#ifdef	INCLUDE_3C503
        /*
         * No way to tell whether or not we're supposed to use
         * the 3Com's transceiver unless the user tells us.
         * 'flags' should have some compile time default value
         * which can be changed from the command menu.
         */
	t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL;
	outb(t503_output, eth_asic_base + _3COM_CR);
#endif
}

static int ns8390_poll(struct nic *nic);

#ifndef	INCLUDE_3C503
/**************************************************************************
ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun
**************************************************************************/
static void eth_rx_overrun(struct nic *nic)
{
	int start_time;

#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);

	/* wait for at least 1.6ms - we wait one timer tick */
	start_time = currticks();
	while (currticks() - start_time <= 1)
		/* Nothing */;

	outb(0, eth_nic_base+D8390_P0_RBCR0);	/* reset byte counter */
	outb(0, eth_nic_base+D8390_P0_RBCR1);

	/*
	 * Linux driver checks for interrupted TX here. This is not necessary,
	 * because the transmit routine waits until the frame is sent.
	 */

	/* enter loopback mode and restart NIC */
	outb(2, eth_nic_base+D8390_P0_TCR);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);

	/* clear the RX ring, acknowledge overrun interrupt */
	eth_drain_receiver = 1;
	while (ns8390_poll(nic))
		/* Nothing */;
	eth_drain_receiver = 0;
	outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR);

	/* leave loopback mode - no packets to be resent (see Linux driver) */
	outb(0, eth_nic_base+D8390_P0_TCR);
}
#endif	/* INCLUDE_3C503 */

/**************************************************************************
NS8390_TRANSMIT - Transmit a frame
**************************************************************************/
static void ns8390_transmit(
	struct nic *nic,
	const char *d,			/* Destination */
	unsigned int t,			/* Type */
	unsigned int s,			/* size */
	const char *p)			/* Packet */
{
#ifdef	INCLUDE_3C503
        if (!(eth_flags & FLAG_PIO)) {
                memcpy((char *)eth_bmem, d, ETH_ALEN);	/* dst */
                memcpy((char *)eth_bmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
                *((char *)eth_bmem+12) = t>>8;		/* type */
                *((char *)eth_bmem+13) = t;
                memcpy((char *)eth_bmem+ETH_HLEN, p, s);
                s += ETH_HLEN;
                while (s < ETH_ZLEN) *((char *)eth_bmem+(s++)) = 0;
        }
#endif

#ifdef	INCLUDE_WD
	/* Memory interface */
	if (eth_flags & FLAG_16BIT) {
		outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
		inb(0x84);
	}
	if (eth_flags & FLAG_790) {
		outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
		inb(0x84);
	}
	inb(0x84);
	memcpy((char *)eth_bmem, d, ETH_ALEN);	/* dst */
	memcpy((char *)eth_bmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
	*((char *)eth_bmem+12) = t>>8;		/* type */
	*((char *)eth_bmem+13) = t;
	memcpy((char *)eth_bmem+ETH_HLEN, p, s);
	s += ETH_HLEN;
	while (s < ETH_ZLEN) *((char *)eth_bmem+(s++)) = 0;
	if (eth_flags & FLAG_790) {
		outb(0, eth_asic_base + WD_MSR);
		inb(0x84);
	}
	if (eth_flags & FLAG_16BIT) {
		outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
		inb(0x84);
	}
#endif

#if	defined(INCLUDE_3C503)
	if (eth_flags & FLAG_PIO) {
#endif
#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM))
		/* Programmed I/O */
		unsigned short type;
		type = (t >> 8) | (t << 8);
		eth_pio_write(d, eth_tx_start<<8, ETH_ALEN);
		eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN);
		/* bcc generates worse code without (const+const) below */
		eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2);
		eth_pio_write(p, (eth_tx_start<<8)+ETH_HLEN, s);
		s += ETH_HLEN;
		if (s < ETH_ZLEN) s = ETH_ZLEN;
#endif
#if	defined(INCLUDE_3C503)
	}
#endif

#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
	outb(s, eth_nic_base+D8390_P0_TBCR0);
	outb(s>>8, eth_nic_base+D8390_P0_TBCR1);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790)
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
	else
#endif
		outb(D8390_COMMAND_PS0 |
			D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
}

/**************************************************************************
NS8390_POLL - Wait for a frame
**************************************************************************/
static int ns8390_poll(struct nic *nic)
{
	int ret = 0;
	unsigned char rstat, curr, next;
	unsigned short len, frag;
	unsigned short pktoff;
	unsigned char *p;
	struct ringbuffer pkthdr;

#ifndef	INCLUDE_3C503
	/* avoid infinite recursion: see eth_rx_overrun() */
	if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) {
		eth_rx_overrun(nic);
		return(0);
	}
#endif	/* INCLUDE_3C503 */
	rstat = inb(eth_nic_base+D8390_P0_RSR);
	if (!(rstat & D8390_RSTAT_PRX)) return(0);
	next = inb(eth_nic_base+D8390_P0_BOUND)+1;
	if (next >= eth_memsize) next = eth_rx_start;
	outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
	curr = inb(eth_nic_base+D8390_P1_CURR);
	outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
	if (curr >= eth_memsize) curr=eth_rx_start;
	if (curr == next) return(0);
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_16BIT) {
		outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
		inb(0x84);
	}
	if (eth_flags & FLAG_790) {
		outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
		inb(0x84);
	}
	inb(0x84);
#endif
	pktoff = next << 8;
	if (eth_flags & FLAG_PIO)
		eth_pio_read(pktoff, (char *)&pkthdr, 4);
	else
		memcpy(&pkthdr, (char *)eth_rmem + pktoff, 4);
	pktoff += sizeof(pkthdr);
	/* incoming length includes FCS so must sub 4 */
	len = pkthdr.len - 4;
	if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
		|| len > ETH_FRAME_LEN) {
		printf("Bogus packet, ignoring\n");
		return (0);
	}
	else {
		p = nic->packet;
		nic->packetlen = len;		/* available to caller */
		frag = (eth_memsize << 8) - pktoff;
		if (len > frag) {		/* We have a wrap-around */
			/* read first part */
			if (eth_flags & FLAG_PIO)
				eth_pio_read(pktoff, p, frag);
			else
				memcpy(p, (char *)eth_rmem + pktoff, frag);
			pktoff = eth_rx_start << 8;
			p += frag;
			len -= frag;
		}
		/* read second part */
		if (eth_flags & FLAG_PIO)
			eth_pio_read(pktoff, p, len);
		else
			memcpy(p, (char *)eth_rmem + pktoff, len);
		ret = 1;
	}
#ifdef	INCLUDE_WD
	if (eth_flags & FLAG_790) {
		outb(0, eth_asic_base + WD_MSR);
		inb(0x84);
	}
	if (eth_flags & FLAG_16BIT) {
		outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
		inb(0x84);
	}
	inb(0x84);
#endif
	next = pkthdr.next;		/* frame number of next packet */
	if (next == eth_rx_start)
		next = eth_memsize;
	outb(next-1, eth_nic_base+D8390_P0_BOUND);
	return(ret);
}

/**************************************************************************
NS8390_DISABLE - Turn off adapter
**************************************************************************/
static void ns8390_disable(struct nic *nic)
{
}

/**************************************************************************
ETH_PROBE - Look for an adapter
**************************************************************************/
#ifdef	INCLUDE_NS8390
struct nic *eth_probe(struct nic *nic, unsigned short *probe_addrs,
		      struct pci_device *pci)
#else
struct nic *eth_probe(struct nic *nic, unsigned short *probe_addrs)
#endif
{
	int i;
	struct wd_board *brd;
	unsigned short chksum;
	unsigned char c;
	eth_vendor = VENDOR_NONE;
	eth_drain_receiver = 0;

#ifdef	INCLUDE_WD
	/******************************************************************
	Search for WD/SMC cards
	******************************************************************/
	for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
		eth_asic_base += 0x20) {
		chksum = 0;
		for (i=8; i<16; i++)
			chksum += inb(eth_asic_base+i);
		/* Extra checks to avoid soundcard */
		if ((chksum & 0xFF) == 0xFF &&
			inb(eth_asic_base+8) != 0xFF &&
			inb(eth_asic_base+9) != 0xFF)
			break;
	}
	if (eth_asic_base > WD_HIGH_BASE)
		return (0);
	/* We've found a board */
	eth_vendor = VENDOR_WD;
	eth_nic_base = eth_asic_base + WD_NIC_ADDR;
	c = inb(eth_asic_base+WD_BID);	/* Get board id */
	for (brd = wd_boards; brd->name; brd++)
		if (brd->id == c) break;
	if (!brd->name) {
		printf("Unknown WD/SMC NIC type %hhX\n", c);
		return (0);	/* Unknown type */
	}
	eth_flags = brd->flags;
	eth_memsize = brd->memsize;
	eth_tx_start = 0;
	eth_rx_start = D8390_TXBUF_SIZE;
	if ((c == TYPE_WD8013EP) &&
		(inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
			eth_flags = FLAG_16BIT;
			eth_memsize = MEM_16384;
	}
	if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
		eth_bmem = (0x80000 |
		 ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
	} else
		eth_bmem = WD_DEFAULT_MEM;
	if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
		*((unsigned int *)(eth_bmem + 8192)) = (unsigned int)0;
		if (*((unsigned int *)(eth_bmem + 8192))) {
			brd += 2;
			eth_memsize = brd->memsize;
		}
	}
	outb(0x80, eth_asic_base + WD_MSR);	/* Reset */
	for (i=0; i<ETH_ALEN; i++) {
		nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR);
	}
	printf("\n%s base %#hx, memory %#hx, addr %!\n",
		brd->name, eth_asic_base, eth_bmem, nic->node_addr);
	if (eth_flags & FLAG_790) {
		outb(WD_MSR_MENB, eth_asic_base+WD_MSR);
		outb((inb(eth_asic_base+0x04) |
			0x80), eth_asic_base+0x04);
		outb((((unsigned)eth_bmem >> 13) & 0x0F) |
			(((unsigned)eth_bmem >> 11) & 0x40) |
			(inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B);
		outb((inb(eth_asic_base+0x04) &
			~0x80), eth_asic_base+0x04);
	} else {
		outb((((unsigned)eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR);
	}
	if (eth_flags & FLAG_16BIT) {
		if (eth_flags & FLAG_790) {
			eth_laar = inb(eth_asic_base + WD_LAAR);
			outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
		} else {
			outb((eth_laar =
				WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR);
/*
	The previous line used to be
				WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
	jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made
	it work for WD8013s.  This seems to work for my 8013 boards. I
	don't know what is really happening.  I wish I had data sheets
	or more time to decode the Linux driver. - Ken
*/
		}
		inb(0x84);
	}
#endif
#ifdef	INCLUDE_3C503
        /******************************************************************
        Search for 3Com 3c503 if no WD/SMC cards
        ******************************************************************/
	if (eth_vendor == VENDOR_NONE) {
		int	idx;
		int	iobase_reg, membase_reg;
		static unsigned short	base[] = {
			0x300, 0x310, 0x330, 0x350,
			0x250, 0x280, 0x2A0, 0x2E0, 0 };

		/* Loop through possible addresses checking each one */

		for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) {

			eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET;
/*
 * Note that we use the same settings for both 8 and 16 bit cards:
 * both have an 8K bank of memory at page 1 while only the 16 bit
 * cards have a bank at page 0.
 */
			eth_memsize = MEM_16384;
			eth_tx_start = 32;
			eth_rx_start = 32 + D8390_TXBUF_SIZE;

		/* Check our base address. iobase and membase should */
		/* both have a maximum of 1 bit set or be 0. */

			iobase_reg = inb(eth_asic_base + _3COM_BCFR);
			membase_reg = inb(eth_asic_base + _3COM_PCFR);

			if ((iobase_reg & (iobase_reg - 1)) ||
				(membase_reg & (membase_reg - 1)))
				continue;		/* nope */

		/* Now get the shared memory address */

			eth_flags = 0;

			switch (membase_reg) {
				case _3COM_PCFR_DC000:
					eth_bmem = 0xdc000;
					break;
				case _3COM_PCFR_D8000:
					eth_bmem = 0xd8000;
					break;
				case _3COM_PCFR_CC000:
					eth_bmem = 0xcc000;
					break;
				case _3COM_PCFR_C8000:
					eth_bmem = 0xc8000;
					break;
				case _3COM_PCFR_PIO:
					eth_flags |= FLAG_PIO;
					eth_bmem = 0;
					break;
				default:
					continue;	/* nope */
				}
			break;
		}

		if (base[idx] == 0)		/* not found */
			return (0);
#ifndef	T503_SHMEM
		eth_flags |= FLAG_PIO;		/* force PIO mode */
		eth_bmem = 0;
#endif
		eth_vendor = VENDOR_3COM;


        /* Need this to make ns8390_poll() happy. */

                eth_rmem = eth_bmem - 0x2000;

        /* Reset NIC and ASIC */

                outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR );
                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR );

        /* Get our ethernet address */

                outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR);
                printf("\n3Com 3c503 base %#hx, ", eth_nic_base);
                if (eth_flags & FLAG_PIO)
			printf("PIO mode");
                else
			printf("memory %#hx", eth_bmem);
                for (i=0; i<ETH_ALEN; i++) {
                        nic->node_addr[i] = inb(eth_nic_base+i);
                }
                printf(", %s, addr %!\n", nic->flags ? "AUI" : "internal xcvr",
			nic->node_addr);
                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR);
        /*
         * Initialize GA configuration register. Set bank and enable shared
         * mem. We always use bank 1. Disable interrupts.
         */
                outb(_3COM_GACFR_RSEL |
			_3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR);

                outb(0xff, eth_asic_base + _3COM_VPTR2);
                outb(0xff, eth_asic_base + _3COM_VPTR1);
                outb(0x00, eth_asic_base + _3COM_VPTR0);
        /*
         * Clear memory and verify that it worked (we use only 8K)
         */

		if (!(eth_flags & FLAG_PIO)) {
			memset((char *)eth_bmem, 0, 0x2000);
			for(i = 0; i < 0x2000; ++i)
				if (*(((char *)eth_bmem)+i)) {
					printf ("Failed to clear 3c503 shared mem.\n");
					return (0);
				}
		}
        /*
         * Initialize GA page/start/stop registers.
         */
                outb(eth_tx_start, eth_asic_base + _3COM_PSTR);
                outb(eth_memsize, eth_asic_base + _3COM_PSPR);
        }
#endif
#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
	/******************************************************************
	Search for NE1000/2000 if no WD/SMC or 3com cards
	******************************************************************/
	if (eth_vendor == VENDOR_NONE) {
		char romdata[16], testbuf[32];
		int idx;
		static char test[] = "NE*000 memory";
		static unsigned short base[] = {
#ifdef	NE_SCAN
			NE_SCAN,
#endif
			0 };
		/* if no addresses supplied, fall back on defaults */
		if (probe_addrs == 0 || probe_addrs[0] == 0)
			probe_addrs = base;
		eth_bmem = 0;		/* No shared memory */
		for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) {
			eth_flags = FLAG_PIO;
			eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
			eth_memsize = MEM_16384;
			eth_tx_start = 32;
			eth_rx_start = 32 + D8390_TXBUF_SIZE;
			c = inb(eth_asic_base + NE_RESET);
			outb(c, eth_asic_base + NE_RESET);
			inb(0x84);
			outb(D8390_COMMAND_STP |
				D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
			outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
			outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
			outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
			outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
#ifdef	NS8390_FORCE_16BIT
			eth_flags |= FLAG_16BIT;	/* force 16-bit mode */
#endif

			eth_pio_write(test, 8192, sizeof(test));
			eth_pio_read(8192, testbuf, sizeof(test));
			if (!memcmp(test, testbuf, sizeof(test)))
				break;
			eth_flags |= FLAG_16BIT;
			eth_memsize = MEM_32768;
			eth_tx_start = 64;
			eth_rx_start = 64 + D8390_TXBUF_SIZE;
			outb(D8390_DCR_WTS |
				D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
			outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
			outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
			eth_pio_write(test, 16384, sizeof(test));
			eth_pio_read(16384, testbuf, sizeof(test));
			if (!memcmp(testbuf, test, sizeof(test)))
				break;
		}
		if (eth_nic_base == 0)
			return (0);
		if (eth_nic_base > ISA_MAX_ADDR)	/* PCI probably */
			eth_flags |= FLAG_16BIT;
		eth_vendor = VENDOR_NOVELL;
		eth_pio_read(0, romdata, sizeof(romdata));
		for (i=0; i<ETH_ALEN; i++) {
			nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
		}
		printf("\nNE%c000 base %#hx, addr %!\n",
			(eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base,
			nic->node_addr);
	}
#endif
	if (eth_vendor == VENDOR_NONE)
		return(0);
        if (eth_vendor != VENDOR_3COM)
		eth_rmem = eth_bmem;
	ns8390_reset(nic);
	nic->reset = ns8390_reset;
	nic->poll = ns8390_poll;
	nic->transmit = ns8390_transmit;
	nic->disable = ns8390_disable;
	return(nic);
}

/*
 * Local variables:
 *  c-basic-offset: 8
 * End:
 */