FreeBSD-5.3/sys/dev/ed/if_ed_cbus.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1995, David Greenman
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/dev/ed/if_ed_cbus.c,v 1.12 2003/10/31 18:31:58 brooks Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/kernel.h>

#include <sys/module.h>
#include <sys/bus.h>
#include <machine/bus.h>
#ifdef PC98
#include <sys/rman.h>
#include <machine/resource.h>
#include <machine/clock.h>
#include <machine/md_var.h>
#endif

#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_mib.h>

#include <isa/isavar.h>

#include <dev/ed/if_edvar.h>
#ifdef PC98
#include <dev/ed/if_edreg.h>
#include <dev/ed/if_ed98.h>

static int ed98_alloc_port	(device_t, int);
static int ed98_alloc_memory	(device_t, int);
static int ed_pio_testmem	(struct ed_softc *, int, int, int);
static int ed_probe_SIC98	(device_t, int, int);
static int ed_probe_CNET98	(device_t, int, int);
static int ed_probe_CNET98EL	(device_t, int, int);
static int ed_probe_NEC77	(device_t, int, int);
static int ed_probe_NW98X	(device_t, int, int);
static int ed_probe_SB98	(device_t, int, int);
static int ed_probe_EZ98	(device_t, int, int);
static int ed98_probe_Novell	(device_t, int, int);
static int ed98_probe_generic8390	(struct ed_softc *);
static void ed_reset_CNET98	(struct ed_softc *, int);
static void ed_winsel_CNET98	(struct ed_softc *, u_short);
static void ed_get_SB98		(struct ed_softc *);
#endif

static int ed_isa_probe		(device_t);
static int ed_isa_attach	(device_t);

static struct isa_pnp_id ed_ids[] = {
#ifdef PC98
/* TODO - list up PnP boards for PC-98 */
	{ 0,		NULL }
#endif
};

static int
ed_isa_probe(dev)
	device_t dev;
{
	struct ed_softc *sc = device_get_softc(dev);
	int flags = device_get_flags(dev);
	int error = 0;

	bzero(sc, sizeof(struct ed_softc));
#ifdef PC98
	sc->type = ED_TYPE98(flags);
#ifdef ED_DEBUG
	device_printf(dev, "ed_isa_probe: sc->type=%x\n", sc->type);
#endif
#endif

	/* Check isapnp ids */
	error = ISA_PNP_PROBE(device_get_parent(dev), dev, ed_ids);
#ifdef ED_DEBUG
	device_printf(dev, "ed_isa_probe: ISA_PNP_PROBE returns %d\n", error);
#endif

	/* If the card had a PnP ID that didn't match any we know about */
	if (error == ENXIO) {
		goto end;
	}

	/* If we had some other problem. */
	if (!(error == 0 || error == ENOENT)) {
		goto end;
	}

	/* Heuristic probes */
#ifdef ED_DEBUG
	device_printf(dev, "ed_isa_probe: Heuristic probes start\n");
#endif
#ifdef PC98
	switch (sc->type) {
	case ED_TYPE98_GENERIC:
		/*
		 * CAUTION!
		 * sc->type of these boards are overwritten by PC/AT's value.
		 */

		/*
		 * SMC EtherEZ98
		 */
		error = ed_probe_EZ98(dev, 0, flags);
		if (error == 0) {
			goto end;
		}

		ed_release_resources(dev);

		/*
		 * Allied Telesis CenterCom LA-98-T
		 */
		error = ed_probe_Novell(dev, 0, flags);
		if (error == 0) {
			goto end;
		}

		break;

	/*
	 * NE2000-like boards probe routine
	 */
	case ED_TYPE98_BDN:
		/*
		 * ELECOM LANEED LD-BDN
		 * PLANET SMART COM 98 EN-2298
		 */
	case ED_TYPE98_LGY:
		/*
		 * MELCO LGY-98, IND-SP, IND-SS
		 * MACNICA NE2098
		 */
	case ED_TYPE98_ICM:
		/*
		 * ICM DT-ET-25, DT-ET-T5, IF-2766ET, IF-2771ET
		 * D-Link DE-298P, DE-298
		 */
	case ED_TYPE98_EGY:
		/*
		 * MELCO EGY-98
		 * Contec C-NET(98)E-A, C-NET(98)L-A
		 */
	case ED_TYPE98_108:
		/*
		 * NEC PC-9801-107,108
		 */
	case ED_TYPE98_NC5098:
		/*
		 * NextCom NC5098
		 */

		error = ed98_probe_Novell(dev, 0, flags);

		break;

	/*
	 * other boards with special probe routine
	 */
	case ED_TYPE98_SIC:
		/*
		 * Allied Telesis SIC-98
		 */
		error = ed_probe_SIC98(dev, 0, flags);

		break;

	case ED_TYPE98_CNET98EL:
		/*
		 * Contec C-NET(98)E/L
		 */
		error = ed_probe_CNET98EL(dev, 0, flags);

		break;

	case ED_TYPE98_CNET98:
		/*
		 * Contec C-NET(98)
		 */
		error = ed_probe_CNET98(dev, 0, flags);

		break;

	case ED_TYPE98_LA98:
		/*
		 * IO-DATA LA/T-98
		 * NEC PC-9801-77,78
		 */
		error = ed_probe_NEC77(dev, 0, flags);

		break;

	case ED_TYPE98_NW98X:
		/*
		 * Networld EC/EP-98X
		 */
		error = ed_probe_NW98X(dev, 0, flags);

		break;

	case ED_TYPE98_SB98:
		/*
		 * Soliton SB-9801
		 * Fujikura FN-9801
		 */

		error = ed_probe_SB98(dev, 0, flags);

		break;
	}
#endif

end:
#ifdef ED_DEBUG
	device_printf(dev, "ed_isa_probe: end, error=%d\n", error);
#endif
	if (error == 0)
		error = ed_alloc_irq(dev, 0, 0);

	ed_release_resources(dev);
	return (error);
}

static int
ed_isa_attach(dev)
	device_t dev;
{
	struct ed_softc *sc = device_get_softc(dev);
	int flags = device_get_flags(dev);
	int error;

	if (sc->port_used > 0) {
#ifdef PC98
		if (ED_TYPE98(flags) == ED_TYPE98_GENERIC) {
			ed_alloc_port(dev, sc->port_rid, sc->port_used);
		} else {
			ed98_alloc_port(dev, sc->port_rid);
		}
#endif
	}
	if (sc->mem_used)
		ed_alloc_memory(dev, sc->mem_rid, sc->mem_used);

	ed_alloc_irq(dev, sc->irq_rid, 0);

	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
			       edintr, sc, &sc->irq_handle);
	if (error) {
		ed_release_resources(dev);
		return (error);
	}

	return ed_attach(dev);
}

#ifdef PC98
/*
 * Interrupt conversion table for EtherEZ98
 */
static unsigned short ed_EZ98_intr_val[] = {
	0,
	3,
	5,
	6,
	0,
	9,
	12,
	13
};

static int
ed_probe_EZ98(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	static unsigned short *intr_vals[] = {NULL, ed_EZ98_intr_val};

	error = ed_alloc_port(dev, port_rid, ED_EZ98_IO_PORTS);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_EZ98_ASIC_OFFSET;
	sc->nic_offset  = ED_EZ98_NIC_OFFSET;

	return ed_probe_WD80x3_generic(dev, flags, intr_vals);
}

/*
 * I/O conversion tables
 */

/* LGY-98, ICM, C-NET(98)E/L */
static	bus_addr_t ed98_ioaddr_generic[] = {
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};

/*
 *		Definitions for Contec C-NET(98)E/L
 */
#define	ED_CNET98EL_ICR         2	/* Interrupt Configuration Register */

#define	ED_CNET98EL_ICR_IRQ3	0x01
#define	ED_CNET98EL_ICR_IRQ5	0x02
#define	ED_CNET98EL_ICR_IRQ6	0x04
#define	ED_CNET98EL_ICR_IRQ12	0x20

#define	ED_CNET98EL_IMR         4	/* Interrupt Mask Register	*/
#define	ED_CNET98EL_ISR         5	/* Interrupt Status Register	*/

/* EGY-98 */
static	bus_addr_t ed98_ioaddr_egy98[] = {
	0,     0x02,  0x04,  0x06,  0x08,  0x0a,  0x0c,  0x0e,
	0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e
};

/* SIC-98 */
static	bus_addr_t ed98_ioaddr_sic98[] = {
	0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0a00, 0x0c00, 0x0e00,
	0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00
};

/* LA/T-98, LD-BDN, PC-9801-77, SB-9801 */
static	bus_addr_t ed98_ioaddr_la98[] = {
	0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000,
	0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000,
	0x0100	/* for NEC 77(see below) */
};

/*
 *		Definitions for NEC PC-9801-77
 */
#define	ED_NEC77_IRQ		16	/* Interrupt Configuration Register */

#define	ED_NEC77_IRQ3		0x04
#define	ED_NEC77_IRQ5		0x06
#define	ED_NEC77_IRQ6		0x08
#define	ED_NEC77_IRQ12		0x0a
#define	ED_NEC77_IRQ13		0x02

/*
 *		Definitions for Soliton SB-9801
 */
#define	ED_SB98_CFG		1	/* Board configuration		*/

#define	ED_SB98_CFG_IRQ3	0x00
#define	ED_SB98_CFG_IRQ5	0x04
#define	ED_SB98_CFG_IRQ6	0x08
#define	ED_SB98_CFG_IRQ12	0x0c
#define	ED_SB98_CFG_ALTPORT	0x40		/* use EXTERNAL media	*/
#define	ED_SB98_CFG_ENABLE	0xa0		/* enable configuration	*/

#define	ED_SB98_EEPENA		2	/* EEPROM access enable		*/

#define	ED_SB98_EEPENA_DISABLE	0x00
#define	ED_SB98_EEPENA_ENABLE	0x01

#define	ED_SB98_EEP		3	/* EEPROM access		*/

#define	ED_SB98_EEP_SDA		0x01		/* Serial Data	*/
#define	ED_SB98_EEP_SCL		0x02		/* Serial Clock	*/
#define	ED_SB98_EEP_READ	0x01		/* Read Command	*/

#define	ED_SB98_EEP_DELAY	300

#define	ED_SB98_ADDRESS		0x01		/* Station Address(1-6)	*/

#define	ED_SB98_POLARITY	4	/* Polarity			*/

/* PC-9801-108 */
static	bus_addr_t ed98_ioaddr_nec108[] = {
	0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e,
	0x1000, 0x1002, 0x1004, 0x1006, 0x1008, 0x100a, 0x100c, 0x100e
};

/* C-NET(98) */
static	bus_addr_t ed98_ioaddr_cnet98[] = {
	0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e,
	0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e
};

/*
 *		Definitions for Contec C-NET(98)
 */
#define	ED_CNET98_MAP_REG0L	0	/* MAPPING register0 Low	*/
#define	ED_CNET98_MAP_REG1L	1	/* MAPPING register1 Low	*/
#define	ED_CNET98_MAP_REG2L	2	/* MAPPING register2 Low	*/
#define	ED_CNET98_MAP_REG3L	3	/* MAPPING register3 Low	*/
#define	ED_CNET98_MAP_REG0H	4	/* MAPPING register0 Hi		*/
#define	ED_CNET98_MAP_REG1H	5	/* MAPPING register1 Hi		*/
#define	ED_CNET98_MAP_REG2H	6	/* MAPPING register2 Hi		*/
#define	ED_CNET98_MAP_REG3H	7	/* MAPPING register3 Hi		*/
#define	ED_CNET98_WIN_REG	8	/* Window register		*/
#define	ED_CNET98_INT_LEV	9	/* Init level register		*/

#define	ED_CNET98_INT_IRQ3	0x01		/* INT 0 */
#define	ED_CNET98_INT_IRQ5	0x02		/* INT 1 */
#define	ED_CNET98_INT_IRQ6	0x04		/* INT 2 */
#define	ED_CNET98_INT_IRQ9	0x08		/* INT 3 */
#define	ED_CNET98_INT_IRQ12	0x20		/* INT 5 */
#define	ED_CNET98_INT_IRQ13	0x40		/* INT 6 */

#define	ED_CNET98_INT_REQ	10	/* Init request register	*/
#define	ED_CNET98_INT_MASK	11	/* Init mask register		*/
#define	ED_CNET98_INT_STAT	12	/* Init status register		*/
#define	ED_CNET98_INT_CLR	12	/* Init clear register		*/
#define	ED_CNET98_RESERVE1	13
#define	ED_CNET98_RESERVE2	14
#define	ED_CNET98_RESERVE3	15

/* EC/EP-98X, NC5098 */
static	bus_addr_t ed98_ioaddr_nw98x[] = {
	0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700,
	0x0800, 0x0900, 0x0a00, 0x0b00, 0x0c00, 0x0d00, 0x0e00, 0x0f00,
	0x1000	/* for EC/EP-98X(see below) */
};

/*
 *		Definitions for Networld EC/EP-98X
 */
#define	ED_NW98X_IRQ            16	/* Interrupt Configuration Register */

#define	ED_NW98X_IRQ3           0x04
#define	ED_NW98X_IRQ5           0x06
#define	ED_NW98X_IRQ6           0x08
#define	ED_NW98X_IRQ12          0x0a
#define	ED_NW98X_IRQ13          0x02

/* NC5098 ASIC */
static bus_addr_t ed98_asic_nc5098[] = {
/*	DATA    ENADDR						RESET	*/
	0x0000, 0x2000, 0x2100, 0x2200, 0x2300, 0x2400, 0x2500, 0x4000,
	     0,      0,      0,      0,      0,      0,      0,      0 
};

/*
 *		Definitions for NextCom NC5098
 */
#define	ED_NC5098_ENADDR	1	/* Station Address(1-6)		*/

/*
 * Allocate a port resource with the given resource id.
 */
static int
ed98_alloc_port(dev, rid)
	device_t dev;
	int rid;
{
	struct ed_softc *sc = device_get_softc(dev);
	struct resource *res;
	int error;
	bus_addr_t *io_nic, *io_asic, adj;
	static bus_addr_t io_res[ED_NOVELL_IO_PORTS + 1];
	int i, n;
	int offset, reset, data;

	/* Set i/o table for resource manager */
	io_nic = io_asic = ed98_ioaddr_generic;
	offset = ED_NOVELL_ASIC_OFFSET;
	reset = ED_NOVELL_RESET;
	data  = ED_NOVELL_DATA;
	n = ED_NOVELL_IO_PORTS;

	switch (sc->type) {
	case ED_TYPE98_LGY:
		io_asic = ed98_ioaddr_egy98; /* XXX - Yes, we use egy98 */
		offset = 0x0200;
		reset = 8;
		break;

	case ED_TYPE98_EGY:
		io_nic = io_asic = ed98_ioaddr_egy98;
		offset = 0x0200;
		reset = 8;
		break;

	case ED_TYPE98_ICM:
		offset = 0x0100;
		break;

	case ED_TYPE98_BDN:
		io_nic = io_asic = ed98_ioaddr_la98;
		offset = 0x0100;
		reset = 0x0c;
		break;

	case ED_TYPE98_SIC:
		io_nic = io_asic = ed98_ioaddr_sic98;
		offset = 0x2000;
		n = 16+1;
		break;

	case ED_TYPE98_108:
		io_nic = io_asic = ed98_ioaddr_nec108;
		offset = 0x0888;	/* XXX - overwritten after */
		reset = 1;
		n = 16;	/* XXX - does not set ASIC i/o here */
		break;

	case ED_TYPE98_LA98:
		io_nic = io_asic = ed98_ioaddr_la98;
		offset = 0x0100;
		break;

	case ED_TYPE98_CNET98EL:
		offset = 0x0400;
		data = 0x0e;
		break;

	case ED_TYPE98_CNET98:
		/* XXX - Yes, we use generic i/o here */
		offset = 0x0400;
		break;

	case ED_TYPE98_NW98X:
		io_nic = io_asic = ed98_ioaddr_nw98x;
		offset = 0x1000;
		break;

	case ED_TYPE98_SB98:
		io_nic = io_asic = ed98_ioaddr_la98;
		offset = 0x0400;
		reset = 7;
		break;

	case ED_TYPE98_NC5098:
		io_nic  = ed98_ioaddr_nw98x;
		io_asic = ed98_asic_nc5098;
		offset = 0x2000;
		reset = 7;
		n = 16+8;	/* XXX */
		break;
	}

	bcopy(io_nic, io_res, sizeof(io_nic[0]) * ED_NOVELL_ASIC_OFFSET);
	for (i = ED_NOVELL_ASIC_OFFSET; i < ED_NOVELL_IO_PORTS; i++) {
		io_res[i] = io_asic[i - ED_NOVELL_ASIC_OFFSET] + offset;
	}

	res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
				  io_res, n, RF_ACTIVE);
	if (!res) {
		return (ENOENT);
	}

	sc->port_rid = rid;
	sc->port_res = res;
	sc->port_used = n;

	/* Re-map i/o table if needed */
	switch (sc->type) {
	case ED_TYPE98_LA98:
	case ED_TYPE98_NW98X:
		io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
		n++;
		break;

	case ED_TYPE98_108:
		adj = (rman_get_start(res) & 0xf000) / 2;
		offset = (offset | adj) - rman_get_start(res);

		for (n = ED_NOVELL_ASIC_OFFSET; n < ED_NOVELL_IO_PORTS; n++) {
			io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
		}
		break;

	case ED_TYPE98_CNET98:
		io_nic = io_asic = ed98_ioaddr_cnet98;
		offset = 1;

		bcopy(io_nic, io_res, sizeof(io_nic[0]) * ED_NOVELL_ASIC_OFFSET);
		for (n = ED_NOVELL_ASIC_OFFSET; n < ED_NOVELL_IO_PORTS; n++) {
			io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
		}
		break;

	case ED_TYPE98_NC5098:
		n = ED_NOVELL_IO_PORTS;
		break;
	}

	if (reset != ED_NOVELL_RESET) {
		io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_RESET] =
			io_res[ED_NOVELL_ASIC_OFFSET + reset];
	}
	if (data  != ED_NOVELL_DATA) {
		io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_DATA] =
			io_res[ED_NOVELL_ASIC_OFFSET + data];
#if 0
		io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_DATA + 1] =
			io_res[ED_NOVELL_ASIC_OFFSET + data + 1];
#endif
	}

	error = isa_load_resourcev(res, io_res, n);
	if (error != 0) {
		return (ENOENT);
	}
#ifdef ED_DEBUG
	device_printf(dev, "ed98_alloc_port: i/o ports = %d\n", n);
	for (i = 0; i < n; i++) {
		printf("%x,", io_res[i]);
	}
	printf("\n");
#endif
	return (0);
}

static int
ed98_alloc_memory(dev, rid)
	device_t dev;
	int rid;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	u_long conf_maddr, conf_msize;

	error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
				 &conf_maddr, &conf_msize);
	if (error) {
		return (error);
	}

	if ((conf_maddr == 0) || (conf_msize == 0)) {
		return (ENXIO);
	}

	error = ed_alloc_memory(dev, rid, (int) conf_msize);
	if (error) {
		return (error);
	}

	sc->mem_start = (caddr_t) rman_get_virtual(sc->mem_res);
	sc->mem_size  = conf_msize;

	return (0);
}

/*
 * Generic probe routine for testing for the existance of a DS8390.
 *	Must be called after the NIC has just been reset. This routine
 *	works by looking at certain register values that are guaranteed
 *	to be initialized a certain way after power-up or reset. Seems
 *	not to currently work on the 83C690.
 *
 * Specifically:
 *
 *	Register			reset bits	set bits
 *	Command Register (CR)		TXP, STA	RD2, STP
 *	Interrupt Status (ISR)				RST
 *	Interrupt Mask (IMR)		All bits
 *	Data Control (DCR)				LAS
 *	Transmit Config. (TCR)		LB1, LB0
 *
 * XXX - We only check the CR register.
 *
 * Return 1 if 8390 was found, 0 if not.
 */

static int
ed98_probe_generic8390(sc)
	struct ed_softc *sc;
{
	u_char tmp = ed_nic_inb(sc, ED_P0_CR);
#ifdef DIAGNOSTIC
	printf("ed?: inb(ED_P0_CR)=%x\n", tmp);
#endif
	if ((tmp & (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
	    (ED_CR_RD2 | ED_CR_STP)) {
		return (0);
	}

	(void) ed_nic_inb(sc, ED_P0_ISR);

	return (1);
}

static int
ed98_probe_Novell(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	int n;
	u_char romdata[ETHER_ADDR_LEN * 2], tmp;

#ifdef ED_DEBUG
	device_printf(dev, "ed98_probe_Novell: start\n");
#endif
	error = ed98_alloc_port(dev, port_rid);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
	sc->nic_offset  = ED_NOVELL_NIC_OFFSET;

	/* Reset the board */
#ifdef ED_DEBUG
	device_printf(dev, "ed98_probe_Novell: reset\n");
#endif
	switch (sc->type) {
#if 1	/* XXX - I'm not sure this is really necessary... */
	case ED_TYPE98_BDN:
		tmp = ed_asic_inb(sc, ED_NOVELL_RESET);
		ed_asic_outb(sc, ED_NOVELL_RESET, (tmp & 0xf0) | 0x08);
		ed_nic_outb(sc, 0x04, tmp);
		(void) ed_asic_inb(sc, 0x08);
		ed_asic_outb(sc, 0x08, tmp);
		ed_asic_outb(sc, 0x08, tmp & 0x7f);
		break;
#endif
	case ED_TYPE98_NC5098:
		ed_asic_outb(sc, ED_NOVELL_RESET, 0x00);
		DELAY(5000);
		ed_asic_outb(sc, ED_NOVELL_RESET, 0x01);
		break;

	default:
		tmp = ed_asic_inb(sc, ED_NOVELL_RESET);

	/*
	 * I don't know if this is necessary; probably cruft leftover from
	 * Clarkson packet driver code. Doesn't do a thing on the boards I've
	 * tested. -DG [note that an outb(0x84, 0) seems to work here, and is
	 * non-invasive...but some boards don't seem to reset and I don't have
	 * complete documentation on what the 'right' thing to do is...so we
	 * do the invasive thing for now. Yuck.]
	 */
		ed_asic_outb(sc, ED_NOVELL_RESET, tmp);
		break;
	}
	DELAY(5000);

	/*
	 * This is needed because some NE clones apparently don't reset the
	 * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
	 * - this makes the probe invasive! ...Done against my better
	 * judgement. -DLG
	 */
	ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
	DELAY(5000);

	/* Make sure that we really have an 8390 based board */
	if (!ed98_probe_generic8390(sc)) {
		return (ENXIO);
	}

	/* Test memory via PIO */
#ifdef ED_DEBUG
	device_printf(dev, "ed98_probe_Novell: test memory\n");
#endif
	sc->cr_proto = ED_CR_RD2;
	if (!ed_pio_testmem(sc,  8192, 0, flags)
	&&  !ed_pio_testmem(sc, 16384, 1, flags)) {
		return (ENXIO);
	}

	/* Setup the board type */
#ifdef ED_DEBUG
	device_printf(dev, "ed98_probe_Novell: board type\n");
#endif
	switch (sc->type) {
	case ED_TYPE98_BDN:
		sc->type_str = "LD-BDN";
		break;
	case ED_TYPE98_EGY:
		sc->type_str = "EGY-98";
		break;
	case ED_TYPE98_LGY:
		sc->type_str = "LGY-98";
		break;
	case ED_TYPE98_ICM:
		sc->type_str = "ICM";
		break;
	case ED_TYPE98_108:
		sc->type_str = "PC-9801-108";
		break;
	case ED_TYPE98_LA98:
		sc->type_str = "LA-98";
		break;
	case ED_TYPE98_NW98X:
		sc->type_str = "NW98X";
		break;
	case ED_TYPE98_NC5098:
		sc->type_str = "NC5098";
		break;
	default:
		sc->type_str = NULL;
		break;
	}

	/* Get station address */
	switch (sc->type) {
	case ED_TYPE98_NC5098:
		for (n = 0; n < ETHER_ADDR_LEN; n++) {
			sc->arpcom.ac_enaddr[n] =
				ed_asic_inb(sc, ED_NC5098_ENADDR + n);
		}
		break;

	default:
		ed_pio_readmem(sc, 0, romdata, sizeof(romdata));
		for (n = 0; n < ETHER_ADDR_LEN; n++) {
			sc->arpcom.ac_enaddr[n] =
				romdata[n * (sc->isa16bit + 1)];
		}
		break;
	}

	/* clear any pending interrupts that might have occurred above */
	ed_nic_outb(sc, ED_P0_ISR, 0xff);

	return (0);
}

/*
 * Probe and vendor-specific initialization routine for SIC-98 boards
 */
static int
ed_probe_SIC98(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	int i;
	u_char sum;

	/*
	 * Setup card RAM and I/O address
	 * Kernel Virtual to segment C0000-DFFFF????
	 */
	error = ed98_alloc_port(dev, port_rid);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_SIC_ASIC_OFFSET;
	sc->nic_offset  = ED_SIC_NIC_OFFSET;

	error = ed98_alloc_memory(dev, 0);
	if (error) {
		return (error);
	}

	/* Reset card to force it into a known state. */
	ed_asic_outb(sc, 0, 0x00);
	DELAY(100);
	if (ED_TYPE98SUB(flags) == 0) {
		/* SIC-98/SIU-98 */
		ed_asic_outb(sc, 0, 0x94);
		DELAY(100);
		ed_asic_outb(sc, 0, 0x94);
	} else {
		/* SIU-98-D */
		ed_asic_outb(sc, 0, 0x80);
		DELAY(100);
		ed_asic_outb(sc, 0, 0x94);
		DELAY(100);
		ed_asic_outb(sc, 0, 0x9e);
	}
	DELAY(100);

	/*
	 * Here we check the card ROM, if the checksum passes, and the
	 * type code and ethernet address check out, then we know we have
	 * an SIC card.
	 */
	sum = sc->mem_start[6 * 2];
	for (i = 0; i < ETHER_ADDR_LEN; i++) {
		sum ^= (sc->arpcom.ac_enaddr[i] = sc->mem_start[i * 2]);
	}
#ifdef ED_DEBUG
	device_printf(dev, "ed_probe_sic98: got address %6D\n",
		      sc->arpcom.ac_enaddr, ":");
#endif
	if (sum != 0) {
		return (ENXIO);
	}
	if ((sc->arpcom.ac_enaddr[0] | sc->arpcom.ac_enaddr[1] |
	     sc->arpcom.ac_enaddr[2]) == 0) {
		return (ENXIO);
	}

	sc->vendor   = ED_VENDOR_SIC;
	sc->type_str = "SIC98";
	sc->isa16bit = 1;
	sc->cr_proto = 0;

	/*
	 * SIC RAM page 0x0000-0x3fff(or 0x7fff)
	 */
	if (ED_TYPE98SUB(flags) == 0) {
		ed_asic_outb(sc, 0, 0x90);
	} else {
		ed_asic_outb(sc, 0, 0x8e);
	}
	DELAY(100);

	/*
	 * clear interface memory, then sum to make sure its valid
	 */
	bzero(sc->mem_start, sc->mem_size);

	for (i = 0; i < sc->mem_size; i++) {
		if (sc->mem_start[i]) {
			device_printf(dev, "failed to clear shared memory "
				"at %x - check configuration\n",
				kvtop(sc->mem_start + i));

			return (ENXIO);
		}
	}

	sc->mem_shared = 1;
	sc->mem_end = sc->mem_start + sc->mem_size;

	/*
	 * allocate one xmit buffer if < 16k, two buffers otherwise
	 */
	if ((sc->mem_size < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
		sc->txb_cnt = 1;
	} else {
		sc->txb_cnt = 2;
	}
	sc->tx_page_start = 0;

	sc->rec_page_start = sc->tx_page_start + ED_TXBUF_SIZE * sc->txb_cnt;
	sc->rec_page_stop = sc->tx_page_start + sc->mem_size / ED_PAGE_SIZE;

	sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;

	return (0);
}

/*
 * Contec C-NET(98) series support routines
 */
static void
ed_reset_CNET98(sc, flags)
	struct ed_softc *sc;
	int flags;
{
	u_int init_addr = ED_CNET98_INIT;
	u_char tmp;

	/* Choose initial register address */
	if (ED_TYPE98SUB(flags) != 0) {
		init_addr = ED_CNET98_INIT2;
	}
#ifdef ED_DEBUG
	printf("ed?: initial register=%x\n", init_addr);
#endif
	/*
	 * Reset the board to force it into a known state.
	 */
	outb(init_addr, 0x00);	/* request */
	DELAY(5000);
	outb(init_addr, 0x01);	/* cancel */
	DELAY(5000);

	/*
	 * Set I/O address(A15-12) and cpu type
	 *
	 *   AAAAIXXC(8bit)
	 *   AAAA: A15-A12,  I: I/O enable, XX: reserved, C: CPU type
	 *
	 * CPU type is 1:80286 or higher, 0:not.
	 * But FreeBSD runs under i386 or higher, thus it must be 1.
	 */
	tmp = (rman_get_start(sc->port_res) & 0xf000) >> 8;
	tmp |= (0x08 | 0x01);
#ifdef ED_DEBUG
	printf("ed?: outb(%x, %x)\n", init_addr + 2, tmp);
#endif
	outb(init_addr + 2, tmp);
	DELAY(5000);

	/*
	 * This is needed because some NE clones apparently don't reset the
	 * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
	 * - this makes the probe invasive! ...Done against my better
	 * judgement. -DLG
	 */
	ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
	DELAY(5000);
}

static void
ed_winsel_CNET98(sc, bank)
	struct ed_softc *sc;
	u_short bank;
{
	u_char mem = (kvtop(sc->mem_start) >> 12) & 0xff;

	/*
	 * Disable window memory
	 *    bit7 is 0:disable
	 */
	ed_asic_outb(sc, ED_CNET98_WIN_REG, mem & 0x7f);
	DELAY(10);

	/*
	 * Select window address
	 *    FreeBSD address 0xf00xxxxx
	 */
	ed_asic_outb(sc, ED_CNET98_MAP_REG0L, bank & 0xff);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG0H, (bank >> 8) & 0xff);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG1L, 0x00);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG1H, 0x41);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG2L, 0x00);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG2H, 0x42);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG3L, 0x00);
	DELAY(10);
	ed_asic_outb(sc, ED_CNET98_MAP_REG3H, 0x43);
	DELAY(10);

	/*
	 * Enable window memory(16Kbyte)
	 *    bit7 is 1:enable
	 */
#ifdef ED_DEBUG
	printf("ed?: window start address=%x\n", mem);
#endif
	ed_asic_outb(sc, ED_CNET98_WIN_REG, mem);
	DELAY(10);
}

/*
 * Probe and vendor-specific initialization routine for C-NET(98) boards
 */
static int
ed_probe_CNET98(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	u_char tmp;
	u_long conf_irq, junk;
	int i;
#ifdef DIAGNOSTIC
	u_char tmp_s;
#endif

	error = ed98_alloc_port(dev, port_rid);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
	sc->nic_offset  = ED_NOVELL_NIC_OFFSET;

	error = ed98_alloc_memory(dev, 0);
	if (error) {
		return (error);
	}

	/* Check I/O address. 0x[a-f]3d0 are allowed. */
	if (((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0)
	||  ((rman_get_start(sc->port_res) & 0xf000) < (u_short) 0xa000)) {
#ifdef DIAGNOSTIC
		device_printf(dev, "Invalid i/o port configuration (0x%lx) "
			"must be %s for %s\n", rman_get_start(sc->port_res),
			"0x[a-f]3d0", "CNET98");
#endif
		return (ENXIO);
	}

#ifdef DIAGNOSTIC
	/* Check window area address */
	tmp_s = kvtop(sc->mem_start) >> 12;
	if (tmp_s < 0x80) {
		device_printf(dev, "Please change window address(0x%x)\n",
			kvtop(sc->mem_start));
		return (ENXIO);
	}

	tmp_s &= 0x0f;
	tmp    = rman_get_start(sc->port_res) >> 12;
	if ((tmp_s <= tmp) && (tmp < (tmp_s + 4))) {
		device_printf(dev, "Please change iobase address(0x%lx) "
			"or window address(0x%x)\n",
	   		rman_get_start(sc->port_res), kvtop(sc->mem_start));
		return (ENXIO);
	}
#endif
	/* Reset the board */
	ed_reset_CNET98(sc, flags);

	/*
	 * This is needed because some NE clones apparently don't reset the
	 * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
	 * - this makes the probe invasive! ...Done against my better
	 * judgement. -DLG
	 */
	ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
	DELAY(5000);

	/* Make sure that we really have an 8390 based board */
	if (!ed98_probe_generic8390(sc)) {
		return (ENXIO);
	}

	/*
	 *  Set window ethernet address area
	 *    board memory base 0x480000  data 256byte
	 */
	ed_winsel_CNET98(sc, 0x4800);

	/*
	 * Get station address from on-board ROM
	 */
	bcopy(sc->mem_start, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);

	sc->vendor    = ED_VENDOR_MISC;
	sc->type_str  = "CNET98";
	sc->isa16bit  = 0;
	sc->cr_proto  = ED_CR_RD2;

	/*
	 * Set window buffer memory area
	 *    board memory base 0x400000  data 16kbyte
	 */
	ed_winsel_CNET98(sc, 0x4000);

	/*
	 * clear interface memory, then sum to make sure its valid
	 */
	bzero(sc->mem_start, sc->mem_size);

	for (i = 0; i < sc->mem_size; i++) {
		if (sc->mem_start[i]) {
			device_printf(dev, "failed to clear shared memory "
				"at %x - check configuration\n",
				kvtop(sc->mem_start + i));

			return (ENXIO);
		}
	}

	sc->mem_shared = 1;
	sc->mem_end = sc->mem_start + sc->mem_size;

	sc->txb_cnt = 1;	/* XXX */
	sc->tx_page_start = 0;

	sc->rec_page_start = sc->tx_page_start + ED_TXBUF_SIZE;
	sc->rec_page_stop = sc->tx_page_start + sc->mem_size / ED_PAGE_SIZE;

	sc->mem_ring = sc->mem_start + ED_PAGE_SIZE * ED_TXBUF_SIZE;

	/*
	 *   Set interrupt level
	 */
	error = bus_get_resource(dev, SYS_RES_IRQ, 0,
				 &conf_irq, &junk);
	if (error)
		return (error);

	switch (conf_irq) {
	case 3:
		tmp = ED_CNET98_INT_IRQ3;
		break;
	case 5:
		tmp = ED_CNET98_INT_IRQ5;
		break;
	case 6:
		tmp = ED_CNET98_INT_IRQ6;
		break;
	case 9:
		tmp = ED_CNET98_INT_IRQ9;
		break;
	case 12:
		tmp = ED_CNET98_INT_IRQ12;
		break;
	case 13:
		tmp = ED_CNET98_INT_IRQ13;
		break;
	default:
		device_printf(dev, "Invalid irq configuration (%ld) must be "
			"%s for %s\n", conf_irq, "3,5,6,9,12,13", "CNET98");
		return (ENXIO);
	}
	ed_asic_outb(sc, ED_CNET98_INT_LEV, tmp);
	DELAY(1000);
	/*
	 *   Set interrupt mask.
	 *     bit7:1 all interrupt mask
	 *     bit1:1 timer interrupt mask
	 *     bit0:0 NS controler interrupt enable
	 */
	ed_asic_outb(sc, ED_CNET98_INT_MASK, 0x7e);
	DELAY(1000);

	return (0);
}

/*
 * Probe and vendor-specific initialization routine for C-NET(98)E/L boards
 */
static int
ed_probe_CNET98EL(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	int i;
	u_char romdata[ETHER_ADDR_LEN * 2], tmp;
	u_long conf_irq, junk;

	error = ed98_alloc_port(dev, port_rid);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
	sc->nic_offset  = ED_NOVELL_NIC_OFFSET;

	/* Check I/O address. 0x[0-f]3d0 are allowed. */
	if ((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0) {
#ifdef DIAGNOSTIC
		device_printf(dev, "Invalid i/o port configuration (0x%lx) "
			"must be %s for %s\n", rman_get_start(sc->port_res),
			"0x?3d0", "CNET98E/L");
#endif
		return (ENXIO);
	}

	/* Reset the board */
	ed_reset_CNET98(sc, flags);

	/*
	 * This is needed because some NE clones apparently don't reset the
	 * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
	 * - this makes the probe invasive! ...Done against my better
	 * judgement. -DLG
	 */
	ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
	DELAY(5000);

	/* Make sure that we really have an 8390 based board */
	if (!ed98_probe_generic8390(sc)) {
		return (ENXIO);
	}

	/* Test memory via PIO */
	sc->cr_proto = ED_CR_RD2;
	if (!ed_pio_testmem(sc, ED_CNET98EL_PAGE_OFFSET, 1, flags)) {
		return (ENXIO);
	}

	/* This looks like a C-NET(98)E/L board. */
	sc->type_str = "CNET98E/L";

	/*
	 * Set IRQ. C-NET(98)E/L only allows a choice of irq 3,5,6.
	 */
	error = bus_get_resource(dev, SYS_RES_IRQ, 0,
				 &conf_irq, &junk);
	if (error) {
		return (error);
	}

	switch (conf_irq) {
	case 3:
		tmp = ED_CNET98EL_ICR_IRQ3;
		break;
	case 5:
		tmp = ED_CNET98EL_ICR_IRQ5;
		break;
	case 6:
		tmp = ED_CNET98EL_ICR_IRQ6;
		break;
#if 0
	case 12:
		tmp = ED_CNET98EL_ICR_IRQ12;
		break;
#endif
	default:
		device_printf(dev, "Invalid irq configuration (%ld) must be "
			"%s for %s\n", conf_irq, "3,5,6", "CNET98E/L");
		return (ENXIO);
	}
	ed_asic_outb(sc, ED_CNET98EL_ICR, tmp);
	ed_asic_outb(sc, ED_CNET98EL_IMR, 0x7e);

	/* Get station address from on-board ROM */
	ed_pio_readmem(sc, 16384, romdata, sizeof(romdata));
	for (i = 0; i < ETHER_ADDR_LEN; i++) {
		sc->arpcom.ac_enaddr[i] = romdata[i * 2];
	}

	/* clear any pending interrupts that might have occurred above */
	ed_nic_outb(sc, ED_P0_ISR, 0xff);

	return (0);
}

/*
 * Probe and vendor-specific initialization routine for PC-9801-77 boards
 */
static int
ed_probe_NEC77(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	u_char tmp;
	u_long conf_irq, junk;

	error = ed98_probe_Novell(dev, port_rid, flags);
	if (error) {
		return (error);
	}

	/* LA/T-98 does not need IRQ setting. */
	if (ED_TYPE98SUB(flags) == 0) {
		return (0);
	}

	/*
	 * Set IRQ. PC-9801-77 only allows a choice of irq 3,5,6,12,13.
	 */
	error = bus_get_resource(dev, SYS_RES_IRQ, 0,
				 &conf_irq, &junk);
	if (error) {
		return (error);
	}

	switch (conf_irq) {
	case 3:
		tmp = ED_NEC77_IRQ3;
		break;
	case 5:
		tmp = ED_NEC77_IRQ5;
		break;
	case 6:
		tmp = ED_NEC77_IRQ6;
		break;
	case 12:
		tmp = ED_NEC77_IRQ12;
		break;
	case 13:
		tmp = ED_NEC77_IRQ13;
		break;
	default:
		device_printf(dev, "Invalid irq configuration (%ld) must be "
			"%s for %s\n", conf_irq, "3,5,6,12,13", "PC-9801-77");
		return (ENXIO);
	}
	ed_asic_outb(sc, ED_NEC77_IRQ, tmp);

	return (0);
}

/*
 * Probe and vendor-specific initialization routine for EC/EP-98X boards
 */
static int
ed_probe_NW98X(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	u_char tmp;
	u_long conf_irq, junk;

	error = ed98_probe_Novell(dev, port_rid, flags);
	if (error) {
		return (error);
	}

	/* Networld 98X3 does not need IRQ setting. */
	if (ED_TYPE98SUB(flags) == 0) {
		return (0);
	}

	/*
	 * Set IRQ. EC/EP-98X only allows a choice of irq 3,5,6,12,13.
	 */
	error = bus_get_resource(dev, SYS_RES_IRQ, 0,
				 &conf_irq, &junk);
	if (error) {
		return (error);
	}

	switch (conf_irq) {
	case 3:
		tmp = ED_NW98X_IRQ3;
		break;
	case 5:
		tmp = ED_NW98X_IRQ5;
		break;
	case 6:
		tmp = ED_NW98X_IRQ6;
		break;
	case 12:
		tmp = ED_NW98X_IRQ12;
		break;
	case 13:
		tmp = ED_NW98X_IRQ13;
		break;
	default:
		device_printf(dev, "Invalid irq configuration (%ld) must be "
			"%s for %s\n", conf_irq, "3,5,6,12,13", "EC/EP-98X");
		return (ENXIO);
	}
	ed_asic_outb(sc, ED_NW98X_IRQ, tmp);

	return (0);
}

/*
 * Read SB-9801 station address from Serial Two-Wire EEPROM
 */
static void
ed_get_SB98(sc)
	struct ed_softc *sc;
{
	int i, j;
	u_char mask, val;

        /* enable EEPROM acceess */
        ed_asic_outb(sc, ED_SB98_EEPENA, ED_SB98_EEPENA_ENABLE);

	/* output start command */
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
	DELAY(ED_SB98_EEP_DELAY);
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
	DELAY(ED_SB98_EEP_DELAY);

       	/* output address (7bit) */
	for (mask = 0x40; mask != 0; mask >>= 1) {
		val = 0;
		if (ED_SB98_ADDRESS & mask)
			val = ED_SB98_EEP_SDA;
		ed_asic_outb(sc, ED_SB98_EEP, val);
		DELAY(ED_SB98_EEP_DELAY);
		ed_asic_outb(sc, ED_SB98_EEP, val | ED_SB98_EEP_SCL);
		DELAY(ED_SB98_EEP_DELAY);
	}

	/* output READ command */
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_READ);
	DELAY(ED_SB98_EEP_DELAY);
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_READ | ED_SB98_EEP_SCL);
	DELAY(ED_SB98_EEP_DELAY);

	/* read station address */
	for (i = 0; i < ETHER_ADDR_LEN; i++) {
		/* output ACK */
		ed_asic_outb(sc, ED_SB98_EEP, 0);
		DELAY(ED_SB98_EEP_DELAY);
		ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
		DELAY(ED_SB98_EEP_DELAY);

		val = 0;
		for (j = 0; j < 8; j++) {
			ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA);
			DELAY(ED_SB98_EEP_DELAY);
			ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
			DELAY(ED_SB98_EEP_DELAY);
			val <<= 1;
			val |= (ed_asic_inb(sc, ED_SB98_EEP) & ED_SB98_EEP_SDA);
			DELAY(ED_SB98_EEP_DELAY);
	  	}
		sc->arpcom.ac_enaddr[i] = val;
	}

	/* output Last ACK */
        ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA);
        DELAY(ED_SB98_EEP_DELAY);
        ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
        DELAY(ED_SB98_EEP_DELAY);

	/* output stop command */
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
	DELAY(ED_SB98_EEP_DELAY);
	ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
	DELAY(ED_SB98_EEP_DELAY);

	/* disable EEPROM access */
	ed_asic_outb(sc, ED_SB98_EEPENA, ED_SB98_EEPENA_DISABLE);
}

/*
 * Probe and vendor-specific initialization routine for SB-9801 boards
 */
static int
ed_probe_SB98(dev, port_rid, flags)
	device_t dev;
	int port_rid;
	int flags;
{
	struct ed_softc *sc = device_get_softc(dev);
	int error;
	u_char tmp;
	u_long conf_irq, junk;

	error = ed98_alloc_port(dev, port_rid);
	if (error) {
		return (error);
	}

	sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
	sc->nic_offset  = ED_NOVELL_NIC_OFFSET;

	/* Check I/O address. 00d[02468ace] are allowed. */
	if ((rman_get_start(sc->port_res) & ~0x000e) != 0x00d0) {
#ifdef DIAGNOSTIC
		device_printf(dev, "Invalid i/o port configuration (0x%lx) "
			"must be %s for %s\n", rman_get_start(sc->port_res),
			"0xd?", "SB9801");
#endif
		return (ENXIO);
	}

	/* Write I/O port address and read 4 times */
	outb(ED_SB98_IO_INHIBIT, rman_get_start(sc->port_res) & 0xff);
	(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
	(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
	(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
	(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);

	/*
	 * Check IRQ. Soliton SB-9801 only allows a choice of
	 * irq 3,5,6,12
	 */
	error = bus_get_resource(dev, SYS_RES_IRQ, 0,
				 &conf_irq, &junk);
	if (error) {
		return (error);
	}

	switch (conf_irq) {
	case 3:
		tmp = ED_SB98_CFG_IRQ3;
		break;
	case 5:
		tmp = ED_SB98_CFG_IRQ5;
		break;
	case 6:
		tmp = ED_SB98_CFG_IRQ6;
		break;
	case 12:
		tmp = ED_SB98_CFG_IRQ12;
		break;
	default:
		device_printf(dev, "Invalid irq configuration (%ld) must be "
			"%s for %s\n", conf_irq, "3,5,6,12", "SB9801");
		return (ENXIO);
	}

	if (flags & ED_FLAGS_DISABLE_TRANCEIVER) {
		tmp |= ED_SB98_CFG_ALTPORT;
	}
	ed_asic_outb(sc, ED_SB98_CFG, ED_SB98_CFG_ENABLE | tmp);
	ed_asic_outb(sc, ED_SB98_POLARITY, 0x01);

	/* Reset the board. */
	ed_asic_outb(sc, ED_NOVELL_RESET, 0x7a);
	DELAY(300);
	ed_asic_outb(sc, ED_NOVELL_RESET, 0x79);
	DELAY(300);

	/*
	 * This is needed because some NE clones apparently don't reset the
	 * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
	 * - this makes the probe invasive! ...Done against my better
	 * judgement. -DLG
	 */
	ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
	DELAY(5000);

	/* Make sure that we really have an 8390 based board */
	if (!ed98_probe_generic8390(sc)) {
		return (ENXIO);
	}

	/* Test memory via PIO */
	sc->cr_proto = ED_CR_RD2;
	if (!ed_pio_testmem(sc, 16384, 1, flags)) {
		return (ENXIO);
	}

	/* This looks like an SB9801 board. */
	sc->type_str = "SB9801";

	/* Get station address */
	ed_get_SB98(sc);

	/* clear any pending interrupts that might have occurred above */
	ed_nic_outb(sc, ED_P0_ISR, 0xff);

	return (0);
}

/*
 * Test the ability to read and write to the NIC memory.
 */
static int
ed_pio_testmem(sc, page_offset, isa16bit, flags)
	struct ed_softc *sc;
	int page_offset;
	int isa16bit;
	int flags;
{
	u_long memsize;
	static char test_pattern[32] = "THIS is A memory TEST pattern";
	char test_buffer[32];
#ifdef DIAGNOSTIC
	int page_end;
#endif

	sc->vendor = ED_VENDOR_NOVELL;
	sc->mem_shared = 0;
	sc->isa16bit = isa16bit;

	/* 8k of memory plus an additional 8k if 16bit */
	memsize = (isa16bit ? 16384 : 8192);

	/*
	 * This prevents packets from being stored in the NIC memory when the
	 * readmem routine turns on the start bit in the CR.
	 */
	ed_nic_outb(sc, ED_P0_RCR, ED_RCR_MON);

	/* Initialize DCR for byte/word operations */
	if (isa16bit) {
		ed_nic_outb(sc, ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS);
	} else {
		ed_nic_outb(sc, ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
	}
	ed_nic_outb(sc, ED_P0_PSTART, page_offset / ED_PAGE_SIZE);
	ed_nic_outb(sc, ED_P0_PSTOP, (page_offset + memsize) / ED_PAGE_SIZE);
#ifdef ED_DEBUG
	printf("ed?: ed_pio_testmem: page start=%x, end=%x",
		      page_offset, page_offset + memsize);
#endif

	/*
	 * Write a test pattern. If this fails, then we don't know
	 * what this board is.
	 */
	ed_pio_writemem(sc, test_pattern, page_offset, sizeof(test_pattern));
	ed_pio_readmem(sc, page_offset, test_buffer, sizeof(test_pattern));

	if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
#ifdef ED_DEBUG
		printf("ed?: ed_pio_testmem: bcmp(page %x) NG",
			      page_offset);
#endif
		return (0);
	}

#ifdef DIAGNOSTIC
	/* Check the bottom. */
	page_end = page_offset + memsize - ED_PAGE_SIZE;
	ed_pio_writemem(sc, test_pattern, page_end, sizeof(test_pattern));
	ed_pio_readmem(sc, page_end, test_buffer, sizeof(test_pattern));

	if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
#ifdef ED_DEBUG
		printf("ed?: ed_pio_testmem: bcmp(page %x) NG",
			      page_end);
#endif
		return (0);
	}
#endif
	sc->mem_size = memsize;
	sc->mem_start = (char *) page_offset;
	sc->mem_end   = sc->mem_start + memsize;
	sc->tx_page_start = page_offset / ED_PAGE_SIZE;

	/*
	 * Use one xmit buffer if < 16k, two buffers otherwise (if not told
	 * otherwise).
	 */
	if ((memsize < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
		sc->txb_cnt = 1;
	} else {
		sc->txb_cnt = 2;
	}

	sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
	sc->rec_page_stop  = sc->tx_page_start + memsize / ED_PAGE_SIZE;

	sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;

	return (1);
}
#endif	/* PC98 */

static device_method_t ed_isa_methods[] = {
	/* Device interface */
	DEVMETHOD(device_probe,		ed_isa_probe),
	DEVMETHOD(device_attach,	ed_isa_attach),

	{ 0, 0 }
};

static driver_t ed_isa_driver = {
	"ed",
	ed_isa_methods,
	sizeof(struct ed_softc)
};

DRIVER_MODULE(ed, isa, ed_isa_driver, ed_devclass, 0, 0);
MODULE_DEPEND(ed, isa, 1, 1, 1);
MODULE_DEPEND(ed, ether, 1, 1, 1);