code:
/*	$OpenBSD: hme.c,v 1.71 2015/07/08 07:21:49 mpi Exp $	*/

/*
 * Copyright (c) 1998 Jason L. Wright (jason@thought.net)
 * 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, 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 ``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 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.
 */

/*
 * Driver for the Happy Meal (hme) ethernet boards
 * Based on information gleaned from reading the
 *	S/Linux driver by David Miller
 *
 * Thanks go to the University of North Carolina at Greensboro, Systems
 * and Networks Department for some of the resources used to develop
 * this driver.
 */

#include "bpfilter.h"
#include "vlan.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/if_media.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif

#include <machine/autoconf.h>
#include <sparc/cpu.h>
#include <sparc/sparc/cpuvar.h>
#include <sparc/dev/sbusvar.h>
#include <sparc/dev/dmareg.h>	/* for SBUS_BURST_* */
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <sparc/dev/hmereg.h>
#include <sparc/dev/hmevar.h>

int	hmematch(struct device *, void *, void *);
void	hmeattach(struct device *, struct device *, void *);
void	hmewatchdog(struct ifnet *);
int	hmeintr(void *);
int	hmeioctl(struct ifnet *, u_long, caddr_t);
void	hmereset(struct hme_softc *);
void	hmestart(struct ifnet *);
void	hmestop(struct hme_softc *);
void	hmeinit(struct hme_softc *);
void	hme_meminit(struct hme_softc *);

void	hme_tick(void *);

void	hme_tcvr_bb_writeb(struct hme_softc *, int);
int	hme_tcvr_bb_readb(struct hme_softc *, int);

void	hme_poll_stop(struct hme_softc *sc);

int	hme_rint(struct hme_softc *);
int	hme_tint(struct hme_softc *);
int	hme_mint(struct hme_softc *, u_int32_t);
int	hme_eint(struct hme_softc *, u_int32_t);

void	hme_reset_rx(struct hme_softc *);
void	hme_reset_tx(struct hme_softc *);

void	hme_read(struct hme_softc *, int, int, u_int32_t);
int	hme_put(struct hme_softc *, int, struct mbuf *);

/*
 * ifmedia glue
 */
int	hme_mediachange(struct ifnet *);
void	hme_mediastatus(struct ifnet *, struct ifmediareq *);

/*
 * mii glue
 */
int	hme_mii_read(struct device *, int, int);
void	hme_mii_write(struct device *, int, int, int);
void	hme_mii_statchg(struct device *);

void	hme_iff(struct hme_softc *);

struct cfattach hme_ca = {
	sizeof (struct hme_softc), hmematch, hmeattach
};

struct cfdriver hme_cd = {
	NULL, "hme", DV_IFNET
};

int
hmematch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	register struct romaux *ra = &ca->ca_ra;

	if (strcmp(cf->cf_driver->cd_name, ra->ra_name) &&
	    strcmp("SUNW,hme", ra->ra_name) &&
	    strcmp("SUNW,qfe", ra->ra_name)) {
		return (0);
	}
	if (!sbus_testdma((struct sbus_softc *)parent, ca))
		return(0);
	return (1);
}

void
hmeattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct confargs *ca = aux;
	struct hme_softc *sc = (struct hme_softc *)self;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int pri;
	struct bootpath *bp;
	/* XXX the following declaration should be elsewhere */
	extern void myetheraddr(u_char *);

	if (ca->ca_ra.ra_nintr != 1) {
		printf(": expected 1 interrupt, got %d\n",
			ca->ca_ra.ra_nintr);
		return;
	}
	pri = ca->ca_ra.ra_intr[0].int_pri;

	/* map registers */
	if (ca->ca_ra.ra_nreg != 5) {
		printf(": expected 5 registers, got %d\n", ca->ca_ra.ra_nreg);
		return;
	}
	sc->sc_gr = mapiodev(&(ca->ca_ra.ra_reg[0]), 0,
			ca->ca_ra.ra_reg[0].rr_len);
	sc->sc_txr = mapiodev(&(ca->ca_ra.ra_reg[1]), 0,
			ca->ca_ra.ra_reg[1].rr_len);
	sc->sc_rxr = mapiodev(&(ca->ca_ra.ra_reg[2]), 0,
			ca->ca_ra.ra_reg[2].rr_len);
	sc->sc_cr = mapiodev(&(ca->ca_ra.ra_reg[3]), 0,
			ca->ca_ra.ra_reg[3].rr_len);
	sc->sc_tcvr = mapiodev(&(ca->ca_ra.ra_reg[4]), 0,
			ca->ca_ra.ra_reg[4].rr_len);

	sc->sc_node = ca->ca_ra.ra_node;

	sc->sc_rev = getpropint(ca->ca_ra.ra_node, "hm-rev", -1);
	if (sc->sc_rev == 0xff)
		sc->sc_rev = 0xa0;
	if (sc->sc_rev == 0x20 || sc->sc_rev == 0x21)
		sc->sc_flags = HME_FLAG_20_21;
	else if (sc->sc_rev != 0xa0)
		sc->sc_flags = HME_FLAG_NOT_A0;

	sc->sc_burst = getpropint(ca->ca_ra.ra_node, "burst-sizes", -1);
	if (sc->sc_burst == -1)
		sc->sc_burst = ((struct sbus_softc *)parent)->sc_burst;

	/* Clamp at parent's burst sizes */
	sc->sc_burst &= ((struct sbus_softc *)parent)->sc_burst;

	hme_meminit(sc);

	sc->sc_ih.ih_fun = hmeintr;
	sc->sc_ih.ih_arg = sc;
	intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih, IPL_NET,
	    self->dv_xname);

	/*
	 * Get MAC address from card if 'local-mac-address' property exists.
	 * Otherwise, use the machine's builtin MAC.
	 */
	if (getprop(ca->ca_ra.ra_node, "local-mac-address",
	    sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN) <= 0) {
		myetheraddr(sc->sc_arpcom.ac_enaddr);
	}

	printf(" pri %d: address %s rev %d\n", pri,
	    ether_sprintf(sc->sc_arpcom.ac_enaddr), sc->sc_rev);

	sc->sc_mii.mii_ifp = ifp;
	sc->sc_mii.mii_readreg = hme_mii_read;
	sc->sc_mii.mii_writereg = hme_mii_write;
	sc->sc_mii.mii_statchg = hme_mii_statchg;
	ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, hme_mediachange,
	    hme_mediastatus);
	mii_phy_probe(self, &sc->sc_mii, 0xffffffff);

	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
		ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_NONE,
		    0, NULL);
		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_NONE);
	}
	else
		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);

	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
	ifp->if_softc = sc;
	ifp->if_start = hmestart;
	ifp->if_ioctl = hmeioctl;
	ifp->if_watchdog = hmewatchdog;
	ifp->if_flags =
		IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
	ifp->if_capabilities = IFCAP_VLAN_MTU;
	IFQ_SET_MAXLEN(&ifp->if_snd, HME_TX_RING_SIZE);
	IFQ_SET_READY(&ifp->if_snd);

	/* Attach the interface. */
	if_attach(ifp);
	ether_ifattach(ifp);

	bp = ca->ca_ra.ra_bp;
	if (bp != NULL && sc->sc_dev.dv_unit == bp->val[1] &&
	    ((strcmp(bp->name, hme_cd.cd_name) == 0) ||
	     (strcmp(bp->name, "qfe") == 0) ||
	     (strcmp(bp->name, "SUNW,hme") == 0)))
		bp->dev = &sc->sc_dev;

	timeout_set(&sc->sc_tick, hme_tick, sc);
}

/*
 * Start output on interface.
 * We make two assumptions here:
 *  1) that the current priority is set to splnet _before_ this code
 *     is called *and* is returned to the appropriate priority after
 *     return
 *  2) that the IFF_OACTIVE flag is checked before this code is called
 *     (i.e. that the output part of the interface is idle)
 */
void
hmestart(ifp)
	struct ifnet *ifp;
{
	struct hme_softc *sc = ifp->if_softc;
	struct mbuf *m;
	int bix, len;

	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
		return;

	bix = sc->sc_last_td;

	for (;;) {
		IFQ_DEQUEUE(&ifp->if_snd, m);
		if (m == NULL)
			break;
#if NBPFILTER > 0
		/*
		 * If BPF is listening on this interface, let it see the
		 * packet before we commit it to the wire.
		 */
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif

		/*
		 * Copy the mbuf chain into the transmit buffer.
		 */
		len = hme_put(sc, bix, m);

		/*
		 * Initialize transmit registers and start transmission.
		 */
		sc->sc_desc->hme_txd[bix].tx_flags =
		    HME_TXD_OWN | HME_TXD_SOP | HME_TXD_EOP |
		    (len & HME_TXD_SIZE);
		sc->sc_txr->tx_pnding = TXR_TP_DMAWAKEUP;

		if (++bix == HME_TX_RING_SIZE)
			bix = 0;

		if (++sc->sc_no_td == HME_TX_RING_SIZE) {
			ifp->if_flags |= IFF_OACTIVE;
			break;
		}
	}

	sc->sc_last_td = bix;
}

#define MAX_STOP_TRIES	16

void
hmestop(sc)
	struct hme_softc *sc;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int tries = 0;

	timeout_del(&sc->sc_tick);

	/*
	 * Mark the interface down and cancel the watchdog timer.
	 */
	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
	ifp->if_timer = 0;

	mii_down(&sc->sc_mii);

	sc->sc_gr->reset = GR_RESET_ALL;
	while (sc->sc_gr->reset && (++tries != MAX_STOP_TRIES))
		DELAY(20);
	if (tries == MAX_STOP_TRIES)
		printf("%s: stop failed\n", sc->sc_dev.dv_xname);
}

/*
 * Reset interface.
 */
void
hmereset(sc)
	struct hme_softc *sc;
{
	int s;

	s = splnet();
	hmestop(sc);
	hmeinit(sc);
	splx(s);
}

void
hme_tick(void *arg)
{
	struct hme_softc *sc = arg;
	int s;

	s = splnet();
	mii_tick(&sc->sc_mii);
	splx(s);

	timeout_add_sec(&sc->sc_tick, 1);
}

/*
 * Device timeout/watchdog routine. Entered if the device neglects to generate
 * an interrupt after a transmit has been started on it.
 */
void
hmewatchdog(ifp)
	struct ifnet *ifp;
{
	struct hme_softc *sc = ifp->if_softc;

	log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
	++sc->sc_arpcom.ac_if.if_oerrors;

	hmereset(sc);
}

int
hmeioctl(ifp, cmd, data)
	struct ifnet *ifp;
	u_long cmd;
	caddr_t data;
{
	struct hme_softc *sc = ifp->if_softc;
	struct ifaddr *ifa = (struct ifaddr *)data;
	struct ifreq *ifr = (struct ifreq *)data;
	int s, error = 0;

	s = splnet();

	switch (cmd) {
	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		if (!(ifp->if_flags & IFF_RUNNING))
			hmeinit(sc);
		if (ifa->ifa_addr->sa_family == AF_INET)
			arp_ifinit(&sc->sc_arpcom, ifa);
		break;

	case SIOCSIFFLAGS:
		if (ifp->if_flags & IFF_UP) {
			if (ifp->if_flags & IFF_RUNNING)
				error = ENETRESET;
			else
				hmeinit(sc);
		} else {
			if (ifp->if_flags & IFF_RUNNING)
				hmestop(sc);
		}
		break;

	case SIOCGIFMEDIA:
	case SIOCSIFMEDIA:
		error = ifmedia_ioctl(ifp, ifr,  &sc->sc_mii.mii_media, cmd);
		break;

	default:
		error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data);
	}

	if (error == ENETRESET) {
		if (ifp->if_flags & IFF_RUNNING)
			hme_iff(sc);
		error = 0;
	}

	splx(s);
	return (error);
}

void
hme_meminit(sc)
	struct hme_softc *sc;
{
	struct hme_desc *desc;
	int i;

	if (sc->sc_desc_dva == NULL)
		sc->sc_desc_dva = (struct hme_desc *) dvma_malloc(
		    sizeof(struct hme_desc), &sc->sc_desc, M_NOWAIT);
	if (sc->sc_bufs_dva == NULL)
		sc->sc_bufs_dva = (struct hme_bufs *) dvma_malloc(
		    sizeof(struct hme_bufs), &sc->sc_bufs, M_NOWAIT);

	desc = sc->sc_desc;

	/*
	 * Setup TX descriptors
	 */
	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
	for (i = 0; i < HME_TX_RING_SIZE; i++) {
		desc->hme_txd[i].tx_addr =
		    (u_int32_t)sc->sc_bufs_dva->tx_buf[i];
		desc->hme_txd[i].tx_flags = 0;
	}

	/*
	 * Setup RX descriptors
	 */
	sc->sc_last_rd = 0;
	for (i = 0; i < HME_RX_RING_SIZE; i++) {
		desc->hme_rxd[i].rx_addr =
		    (u_int32_t)sc->sc_bufs_dva->rx_buf[i];
		desc->hme_rxd[i].rx_flags = HME_RXD_OWN |
		    ((HME_RX_PKT_BUF_SZ - HME_RX_OFFSET) << 16);
	}
}

void
hmeinit(sc)
	struct hme_softc *sc;
{
	u_int32_t c;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct hme_tcvr *tcvr = sc->sc_tcvr;
	struct hme_cr *cr = sc->sc_cr;
	struct hme_gr *gr = sc->sc_gr;
	struct hme_txr *txr = sc->sc_txr;
	struct hme_rxr *rxr = sc->sc_rxr;

	hme_poll_stop(sc);
	hmestop(sc);

	hme_meminit(sc);

	tcvr->int_mask = 0xffff;

	c = tcvr->cfg;
	if (sc->sc_flags & HME_FLAG_FENABLE)
		tcvr->cfg = c & ~(TCVR_CFG_BENABLE);
	else
		tcvr->cfg = c | TCVR_CFG_BENABLE;

	hme_reset_tx(sc);
	hme_reset_rx(sc);

	cr->rand_seed = sc->sc_arpcom.ac_enaddr[5] |
	    ((sc->sc_arpcom.ac_enaddr[4] << 8) & 0x3f00);
	cr->mac_addr0 = (sc->sc_arpcom.ac_enaddr[0] << 8) |
			   sc->sc_arpcom.ac_enaddr[1];
	cr->mac_addr1 = (sc->sc_arpcom.ac_enaddr[2] << 8) |
			   sc->sc_arpcom.ac_enaddr[3];
	cr->mac_addr2 = (sc->sc_arpcom.ac_enaddr[4] << 8) |
			   sc->sc_arpcom.ac_enaddr[5];
	cr->tx_pkt_max = cr->rx_pkt_max = ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN;

	cr->jsize = HME_DEFAULT_JSIZE;
	cr->ipkt_gap1 = HME_DEFAULT_IPKT_GAP1;
	cr->ipkt_gap2 = HME_DEFAULT_IPKT_GAP2;

	rxr->rx_ring = (u_int32_t)sc->sc_desc_dva->hme_rxd;
	txr->tx_ring = (u_int32_t)sc->sc_desc_dva->hme_txd;

	if (sc->sc_burst & SBUS_BURST_64)
		gr->cfg = GR_CFG_BURST64;
	else if (sc->sc_burst & SBUS_BURST_32)
		gr->cfg = GR_CFG_BURST32;
	else if (sc->sc_burst & SBUS_BURST_16)
		gr->cfg = GR_CFG_BURST16;
	else {
		printf("%s: burst size unknown\n", sc->sc_dev.dv_xname);
		gr->cfg = 0;
	}

	gr->imask = GR_IMASK_SENTFRAME | GR_IMASK_TXPERR |
	              GR_IMASK_GOTFRAME | GR_IMASK_RCNTEXP;

	txr->tx_rsize = (HME_TX_RING_SIZE >> TXR_RSIZE_SHIFT) - 1;
	txr->cfg |= TXR_CFG_DMAENABLE;

	c = RXR_CFG_DMAENABLE | (HME_RX_OFFSET << 3);
#if HME_RX_RING_SIZE == 32
	c |= RXR_CFG_RINGSIZE32;
#elif HME_RX_RING_SIZE == 64
	c |= RXR_CFG_RINGSIZE64;
#elif HME_RX_RING_SIZE == 128
	c |= RXR_CFG_RINGSIZE128;
#elif HME_RX_RING_SIZE == 256
	c |= RXR_CFG_RINGSIZE256;
#else
#error "HME_RX_RING_SIZE must be 32, 64, 128, or 256."
#endif
	rxr->cfg = c;
	DELAY(20);
	if (c != rxr->cfg)	/* the receiver sometimes misses bits */
		printf("%s: setting rxreg->cfg failed.\n", sc->sc_dev.dv_xname);

	cr->rx_cfg = 0;
	hme_iff(sc);
	DELAY(10);

	cr->tx_cfg |= CR_TXCFG_DGIVEUP;

	c = CR_XCFG_ODENABLE;
	if (sc->sc_flags & HME_FLAG_LANCE)
		c |= (HME_DEFAULT_IPKT_GAP0 << 5) | CR_XCFG_LANCE;
	cr->xif_cfg = c;

	cr->tx_cfg |= CR_TXCFG_ENABLE;	/* enable tx */
	cr->rx_cfg |= CR_RXCFG_ENABLE;	/* enable rx */

	mii_mediachg(&sc->sc_mii);

	timeout_add_sec(&sc->sc_tick, 1);

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;
}

void
hme_poll_stop(sc)
	struct hme_softc *sc;
{
	struct hme_tcvr *tcvr = sc->sc_tcvr;

	/* if not polling, or polling not enabled, we're done. */
	if ((sc->sc_flags & (HME_FLAG_POLLENABLE | HME_FLAG_POLL)) !=
	    (HME_FLAG_POLLENABLE | HME_FLAG_POLL))
		return;

	/* Turn off MIF interrupts, and disable polling */
	tcvr->int_mask = 0xffff;
	tcvr->cfg &= ~(TCVR_CFG_PENABLE);
	sc->sc_flags &= ~(HME_FLAG_POLL);
	DELAY(200);
}

#define RESET_TRIES	32

void
hme_reset_tx(sc)
	struct hme_softc *sc;
{
	int tries = RESET_TRIES;
	struct hme_cr *cr = sc->sc_cr;

	cr->tx_swreset = 0;
	while (--tries && (cr->tx_swreset & 1))
		DELAY(20);

	if (!tries)
		printf("%s: reset tx failed\n", sc->sc_dev.dv_xname);
}

void
hme_reset_rx(sc)
	struct hme_softc *sc;
{
	int tries = RESET_TRIES;
	struct hme_cr *cr = sc->sc_cr;

	cr->rx_swreset = 0;
	while (--tries && (cr->rx_swreset & 1))
		DELAY(20);

	if (!tries)
		printf("%s: reset rx failed\n", sc->sc_dev.dv_xname);
}

/*
 * mif interrupt
 */
int
hme_mint(sc, why)
	struct hme_softc *sc;
	u_int32_t why;
{
	printf("%s: link status changed\n", sc->sc_dev.dv_xname);
	hme_poll_stop(sc);
	return (1);
}

/*
 * transmit interrupt
 */
int
hme_tint(sc)
	struct hme_softc *sc;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct hme_cr *cr = sc->sc_cr;
	int bix;
	struct hme_txd txd;

	/*
	 * Get collision counters
	 */
	ifp->if_collisions += cr->ex_ctr + cr->lt_ctr + cr->fc_ctr + cr->nc_ctr;
	cr->ex_ctr = 0;
	cr->lt_ctr = 0;
	cr->fc_ctr = 0;
	cr->nc_ctr = 0;

	bix = sc->sc_first_td;

	for (;;) {
		if (sc->sc_no_td <= 0)
			break;

		bcopy(&sc->sc_desc->hme_txd[bix], &txd, sizeof(txd));

		if (txd.tx_flags & HME_TXD_OWN)
			break;

		ifp->if_flags &= ~IFF_OACTIVE;
		ifp->if_opackets++;

		if (++bix == HME_TX_RING_SIZE)
			bix = 0;

		--sc->sc_no_td;
	}

	sc->sc_first_td = bix;

	hmestart(ifp);

	if (sc->sc_no_td == 0)
		ifp->if_timer = 0;

	return (1);
}

int
hme_rint(sc)
	struct hme_softc *sc;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int bix, len;
	struct hme_rxd rxd;

	bix = sc->sc_last_rd;

	for (;;) {
		bcopy(&sc->sc_desc->hme_rxd[bix], &rxd, sizeof(rxd));
		len = rxd.rx_flags >> 16;

		if (rxd.rx_flags & HME_RXD_OWN)
			break;

		if (rxd.rx_flags & HME_RXD_OVERFLOW)
			ifp->if_ierrors++;
		else
			hme_read(sc, bix, len, rxd.rx_flags);

		rxd.rx_flags = HME_RXD_OWN |
		    ((HME_RX_PKT_BUF_SZ - HME_RX_OFFSET) << 16);
		bcopy(&rxd, &sc->sc_desc->hme_rxd[bix], sizeof(rxd));

		if (++bix == HME_RX_RING_SIZE)
			bix = 0;
	}

	sc->sc_last_rd = bix;

	return (1);
}

/*
 * error interrupt
 */
int
hme_eint(sc, why)
	struct hme_softc *sc;
	u_int32_t why;
{
	if (why & GR_STAT_NORXD) {
		sc->sc_arpcom.ac_if.if_ierrors++;
		why &= ~GR_STAT_NORXD;
	}
	if (why & GR_STAT_DTIMEXP) {
		sc->sc_arpcom.ac_if.if_oerrors++;
		why &= ~GR_STAT_DTIMEXP;
	}

	if (why & GR_STAT_ALL_ERRORS) {
		printf("%s: stat=%b, resetting.\n", sc->sc_dev.dv_xname,
		    why, GR_STAT_BITS);
		hmereset(sc);
	}

	return (1);
}

/*
 * Interrupt handler
 */
int
hmeintr(v)
	void *v;
{
	struct hme_softc *sc = (struct hme_softc *)v;
	struct hme_gr *gr = sc->sc_gr;
	u_int32_t why;
	int r = 0;

	why = gr->stat;

	if (why & GR_STAT_ALL_ERRORS)
		r |= hme_eint(sc, why);

	if (why & GR_STAT_MIFIRQ)
		r |= hme_mint(sc, why);

	if (why & (GR_STAT_TXALL | GR_STAT_HOSTTOTX))
		r |= hme_tint(sc);

	if (why & GR_STAT_RXTOHOST)
		r |= hme_rint(sc);

	return (r);
}

int
hme_put(sc, idx, m)
	struct hme_softc *sc;
	int idx;
	struct mbuf *m;
{
	struct mbuf *n;
	u_int8_t *buf = sc->sc_bufs->tx_buf[idx];
	int len, tlen = 0;

	for (; m; m = n) {
		len = m->m_len;
		if (len == 0) {
			n = m_free(m);
			continue;
		}
		bcopy(mtod(m, caddr_t), buf, len);
		buf += len;
		tlen += len;
		n = m_free(m);
	}
	return (tlen);
}

void
hme_read(sc, idx, len, flags)
	struct hme_softc *sc;
	int idx, len;
	u_int32_t flags;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
	struct mbuf *m;

	if (len <= sizeof(struct ether_header) ||
	    len > ETHERMTU + sizeof(struct ether_header)) {
		printf("%s: invalid packet size %d; dropping\n",
		    ifp->if_xname, len);
		ifp->if_ierrors++;
		return;
	}

	/* Pull packet off interface. */
	m = m_devget(sc->sc_bufs->rx_buf[idx] + HME_RX_OFFSET, len,
	    HME_RX_OFFSET);
	if (m == NULL) {
		ifp->if_ierrors++;
		return;
	}

	ml_enqueue(&ml, m);
	if_input(ifp, &ml);
}

void
hme_iff(sc)
	struct hme_softc *sc;
{
	struct arpcom *ac = &sc->sc_arpcom;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct hme_cr *cr = sc->sc_cr;
	struct ether_multi *enm;
	struct ether_multistep step;
	u_int32_t rxcfg, crc;
	u_int32_t hash[4];

	rxcfg = cr->rx_cfg;
	rxcfg &= ~(CR_RXCFG_HENABLE | CR_RXCFG_PMISC);
	ifp->if_flags &= ~IFF_ALLMULTI;
	/* Clear hash table */
	hash[0] = hash[1] = hash[2] = hash[3] = 0;

	if (ifp->if_flags & IFF_PROMISC) {
		ifp->if_flags |= IFF_ALLMULTI;
		rxcfg |= CR_RXCFG_PMISC;
	} else if (ac->ac_multirangecnt > 0) {
		ifp->if_flags |= IFF_ALLMULTI;
		rxcfg |= CR_RXCFG_HENABLE;
		hash[0] = hash[1] = hash[2] = hash[3] = 0xffff;
	} else {
		rxcfg |= CR_RXCFG_HENABLE;

		ETHER_FIRST_MULTI(step, ac, enm);
		while (enm != NULL) {
			crc = ether_crc32_le(enm->enm_addrlo,
			    ETHER_ADDR_LEN) >> 26;

			/* Set the corresponding bit in the filter. */
			hash[crc >> 4] |= 1 << (crc & 0xf);

			ETHER_NEXT_MULTI(step, enm);
		}
	}

	/* Now load the hash table into the chip */
	cr->htable0 = hash[0];
	cr->htable1 = hash[1];
	cr->htable2 = hash[2];
	cr->htable3 = hash[3];
	cr->rx_cfg = rxcfg;
}

/*
 * Writing to the serial BitBang, is a matter of putting the bit
 * into the data register, then strobing the clock.
 */
void
hme_tcvr_bb_writeb(sc, b)
	struct hme_softc *sc;
	int b;
{
	sc->sc_tcvr->bb_data = b & 0x1;
	sc->sc_tcvr->bb_clock = 0;
	sc->sc_tcvr->bb_clock = 1;
}

/*
 * Read a bit from a PHY, if the PHY is not our internal or external
 * phy addr, just return all zero's.
 */
int
hme_tcvr_bb_readb(sc, phy)
	struct hme_softc *sc;
	int phy;
{
	int ret;

	sc->sc_tcvr->bb_clock = 0;
	DELAY(10);

	if (phy == TCVR_PHYADDR_ITX)
		ret = sc->sc_tcvr->cfg & TCVR_CFG_MDIO0;
	else if (phy == TCVR_PHYADDR_ETX)
		ret = sc->sc_tcvr->cfg & TCVR_CFG_MDIO1;
	else
		ret = 0;

	sc->sc_tcvr->bb_clock = 1;

	return ((ret) ? 1 : 0);
}

void
hme_mii_write(self, phy, reg, val)
	struct device *self;
	int phy, reg, val;
{
	struct hme_softc *sc = (struct hme_softc *)self;
	struct hme_tcvr *tcvr = sc->sc_tcvr;
	int tries = 16, i;

	if (sc->sc_flags & HME_FLAG_FENABLE) {
		tcvr->frame = (FRAME_WRITE | phy << 23) |
		    ((reg & 0xff) << 18) | (val & 0xffff);
		while (!(tcvr->frame & 0x10000) && (tries != 0)) {
			tries--;
			DELAY(200);
		}
		if (!tries)
			printf("%s: mii_write failed\n", sc->sc_dev.dv_xname);
		return;
	}

	tcvr->bb_oenab = 1;

	for (i = 0; i < 32; i++)
		hme_tcvr_bb_writeb(sc, 1);

	hme_tcvr_bb_writeb(sc, (MII_COMMAND_START >> 1) & 1);
	hme_tcvr_bb_writeb(sc, MII_COMMAND_START & 1);
	hme_tcvr_bb_writeb(sc, (MII_COMMAND_WRITE >> 1) & 1);
	hme_tcvr_bb_writeb(sc, MII_COMMAND_WRITE & 1);

	for (i = 4; i >= 0; i--)
		hme_tcvr_bb_writeb(sc, (phy >> i) & 1);

	for (i = 4; i >= 0; i--)
		hme_tcvr_bb_writeb(sc, (reg >> i) & 1);

	for (i = 15; i >= 0; i--)
		hme_tcvr_bb_writeb(sc, (reg >> i) & 1);

	tcvr->bb_oenab = 0;
}

int
hme_mii_read(self, phy, reg)
	struct device *self;
	int phy, reg;
{
	struct hme_softc *sc = (struct hme_softc *)self;
	struct hme_tcvr *tcvr = sc->sc_tcvr;
	int tries = 16, i, ret = 0;

	/* Use the frame if possible */
	if (sc->sc_flags & HME_FLAG_FENABLE) {
		tcvr->frame = (FRAME_READ | phy << 23) |
		    ((reg & 0xff) << 18);
		while (!(tcvr->frame & 0x10000) && (tries != 0)) {
			tries--;
			DELAY(20);
		}
		if (!tries) {
			printf("%s: mii_read failed\n", sc->sc_dev.dv_xname);
			return (0);
		}
		return (tcvr->frame & 0xffff);
	}

	tcvr->bb_oenab = 1;

	for (i = 0; i < 32; i++)		/* make bitbang idle */
		hme_tcvr_bb_writeb(sc, 1);

	hme_tcvr_bb_writeb(sc, (MII_COMMAND_START >> 1) & 1);
	hme_tcvr_bb_writeb(sc, MII_COMMAND_START & 1);
	hme_tcvr_bb_writeb(sc, (MII_COMMAND_READ >> 1) & 1);
	hme_tcvr_bb_writeb(sc, MII_COMMAND_READ & 1);

	for (i = 4; i >= 0; i--)
		hme_tcvr_bb_writeb(sc, (phy >> i) & 1);

	for (i = 4; i >= 0; i--)
		hme_tcvr_bb_writeb(sc, (reg >> i) & 1);

	tcvr->bb_oenab = 0;			/* turn off bitbang intrs */

	hme_tcvr_bb_readb(sc, phy);		/* ignore... */

	for (i = 15; i >= 15; i--)		/* read value */
		ret |= hme_tcvr_bb_readb(sc, phy) << i;

	hme_tcvr_bb_readb(sc, phy);			/* ignore... */
	hme_tcvr_bb_readb(sc, phy);			/* ignore... */
	hme_tcvr_bb_readb(sc, phy);			/* ignore... */

	return (ret);
}

int
hme_mediachange(ifp)
	struct ifnet *ifp;
{
	if (ifp->if_flags & IFF_UP)
		hmeinit(ifp->if_softc);
	return (0);
}

void
hme_mediastatus(ifp, ifmr)
	struct ifnet *ifp;
	struct ifmediareq *ifmr;
{
	struct hme_softc *sc = (struct hme_softc *)ifp->if_softc;

	mii_pollstat(&sc->sc_mii);
	ifmr->ifm_active = sc->sc_mii.mii_media_active;
	ifmr->ifm_status = sc->sc_mii.mii_media_status;
}

void
hme_mii_statchg(self)
	struct device *self;
{
	struct hme_softc *sc = (struct hme_softc *)self;
	struct hme_cr *cr = sc->sc_cr;

	/* Apparently the hme chip is SIMPLEX if working in full duplex mode,
	   but not otherwise. */
	if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) != 0) {
		cr->tx_cfg |= CR_TXCFG_FULLDPLX;
		sc->sc_arpcom.ac_if.if_flags |= IFF_SIMPLEX;
	} else {
		cr->tx_cfg &= ~CR_TXCFG_FULLDPLX;
		sc->sc_arpcom.ac_if.if_flags &= ~IFF_SIMPLEX;
	}
}


code:
/*	$OpenBSD: hmereg.h,v 1.12 2005/02/22 20:44:26 brad Exp $	*/

/*
 * Copyright (c) 1998 Jason L. Wright (jason@thought.net)
 * 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, 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 ``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 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.
 */

#define HME_DEFAULT_JSIZE	4
#define HME_DEFAULT_IPKT_GAP0	16
#define HME_DEFAULT_IPKT_GAP1	8
#define HME_DEFAULT_IPKT_GAP2	4

/* global registers */
struct hme_gr {
	volatile u_int32_t	reset;		/* reset tx/rx */
	volatile u_int32_t	cfg;		/* config */
	volatile u_int32_t	_padding[62];	/* unused */
	volatile u_int32_t	stat;		/* intr status */
	volatile u_int32_t	imask;		/* intr mask */
};

/* hme_gr.reset (software reset register) */
#define GR_RESET_ETX		0x01	/* reset external tx */
#define GR_RESET_ERX		0x02	/* reset external rx */
#define GR_RESET_ALL		(GR_RESET_ETX | GR_RESET_ERX)

/* hme_gr.cfg (configuration register) */
#define GR_CFG_BURSTMSK		0x03	/* burst mask */
#define GR_CFG_BURST16		0x00	/* 16 byte bursts */
#define GR_CFG_BURST32		0x01	/* 32 byte bursts */
#define GR_CFG_BURST64		0x02	/* 32 byte bursts */
#define GR_CFG_64BIT		0x04
#define GR_CFG_PARITY		0x08
#define GR_CFG_RESV		0x10

/* hme_gr.stat (interrupt status register) */
#define GR_STAT_GOTFRAME	0x00000001 /* frame received */
#define GR_STAT_RCNTEXP		0x00000002 /* rx frame count expired */
#define GR_STAT_ACNTEXP		0x00000004 /* align error count expired */
#define GR_STAT_CCNTEXP		0x00000008 /* crc error count expired */
#define GR_STAT_LCNTEXP		0x00000010 /* length error count expired */
#define GR_STAT_RFIFOVF		0x00000020 /* rx fifo overflow */
#define GR_STAT_CVCNTEXP	0x00000040 /* code violation counter expired */
#define GR_STAT_STSTERR		0x00000080 /* xif sqe test failed */
#define GR_STAT_SENTFRAME	0x00000100 /* frame sent */
#define GR_STAT_TFIFO_UND	0x00000200 /* tx fifo underrun */
#define GR_STAT_MAXPKTERR	0x00000400 /* max-packet size error */
#define GR_STAT_NCNTEXP		0x00000800 /* normal collision count expired */
#define GR_STAT_ECNTEXP		0x00001000 /* excess collision count expired */
#define GR_STAT_LCCNTEXP	0x00002000 /* late collision count expired */
#define GR_STAT_FCNTEXP		0x00004000 /* first collision count expired */
#define GR_STAT_DTIMEXP		0x00008000 /* defer timer expired */
#define GR_STAT_RXTOHOST	0x00010000 /* pkt moved from rx fifo->memory */
#define GR_STAT_NORXD		0x00020000 /* out of receive descriptors */
#define GR_STAT_RXERR		0x00040000 /* rx dma error */
#define GR_STAT_RXLATERR	0x00080000 /* late error during rx dma */
#define GR_STAT_RXPERR		0x00100000 /* parity error during rx dma */
#define GR_STAT_RXTERR		0x00200000 /* tag error during rx dma */
#define GR_STAT_EOPERR		0x00400000 /* tx descriptor did not set EOP */
#define GR_STAT_MIFIRQ		0x00800000 /* mif needs attention */
#define GR_STAT_HOSTTOTX	0x01000000 /* pkt moved from memory->tx fifo */
#define GR_STAT_TXALL		0x02000000 /* all pkts in fifo transmitted */
#define GR_STAT_TXEACK		0x04000000 /* error during tx dma */
#define GR_STAT_TXLERR		0x08000000 /* late error during tx dma */
#define GR_STAT_TXPERR		0x10000000 /* parity error during tx dma */
#define GR_STAT_TXTERR		0x20000000 /* tag error durig tx dma */
#define GR_STAT_SLVERR		0x40000000 /* pio access error */
#define GR_STAT_SLVPERR		0x80000000 /* pio access parity error */

/* all the errors to worry about */
#define GR_STAT_ALL_ERRORS	\
	(GR_STAT_SLVPERR   | GR_STAT_SLVERR  | GR_STAT_TXTERR    | \
	 GR_STAT_TXPERR    | GR_STAT_TXLERR  | GR_STAT_TXEACK    | \
	 GR_STAT_EOPERR    | GR_STAT_RXTERR  | GR_STAT_RXPERR    | \
	 GR_STAT_RXLATERR  | GR_STAT_RXERR   | GR_STAT_NORXD     | \
	 GR_STAT_DTIMEXP   | GR_STAT_FCNTEXP | GR_STAT_LCCNTEXP  | \
	 GR_STAT_ECNTEXP   | GR_STAT_NCNTEXP | GR_STAT_MAXPKTERR | \
	 GR_STAT_TFIFO_UND | GR_STAT_STSTERR | GR_STAT_CVCNTEXP  | \
	 GR_STAT_RFIFOVF   | GR_STAT_LCNTEXP | GR_STAT_CCNTEXP   | \
	 GR_STAT_ACNTEXP)

#define	GR_STAT_BITS	\
	"\20\1RX\2RCNT\3ACNT\4CCNT\5LCNT\6RFIFO\7CVCNT\10STST" \
	"\11TX\12TFIFO\13MAXPKT\14NCNT\15ECNT\16LCCNT\17FCNT" \
	"\20DTIME\21RXHOST\22NORXD\23RXE\24EXLATE\25RXP\26RXT\27EOP" \
	"\30MIF\31TXHOST\32TXALL\33TXE\34TXL\35TXP\36TXT\37SLV" \
	"\40SLVP"

/* hme_gr.stat (interrupt status register) */
#define GR_IMASK_GOTFRAME	0x00000001 /* frame received */
#define GR_IMASK_RCNTEXP	0x00000002 /* rx frame count expired */
#define GR_IMASK_ACNTEXP	0x00000004 /* align error count expired */
#define GR_IMASK_CCNTEXP	0x00000008 /* crc error count expired */
#define GR_IMASK_LCNTEXP	0x00000010 /* length error count expired */
#define GR_IMASK_RFIFOVF	0x00000020 /* rx fifo overflow */
#define GR_IMASK_CVCNTEXP	0x00000040 /* code violation count expired */
#define GR_IMASK_STSTERR	0x00000080 /* xif sqe test failed */
#define GR_IMASK_SENTFRAME	0x00000100 /* frame sent */
#define GR_IMASK_TFIFO_UND	0x00000200 /* tx fifo underrun */
#define GR_IMASK_MAXPKTERR	0x00000400 /* max-packet size error */
#define GR_IMASK_NCNTEXP	0x00000800 /* normal collision count expired */
#define GR_IMASK_ECNTEXP	0x00001000 /* excess collision count expired */
#define GR_IMASK_LCCNTEXP	0x00002000 /* late collision count expired */
#define GR_IMASK_FCNTEXP	0x00004000 /* first collision count expired */
#define GR_IMASK_DTIMEXP	0x00008000 /* defer timer expired */
#define GR_IMASK_RXTOHOST	0x00010000 /* pkt moved from rx fifo->memory */
#define GR_IMASK_NORXD		0x00020000 /* out of receive descriptors */
#define GR_IMASK_RXERR		0x00040000 /* rx dma error */
#define GR_IMASK_RXLATERR	0x00080000 /* late error during rx dma */
#define GR_IMASK_RXPERR		0x00100000 /* parity error during rx dma */
#define GR_IMASK_RXTERR		0x00200000 /* tag error during rx dma */
#define GR_IMASK_EOPERR		0x00400000 /* tx descriptor did not set EOP */
#define GR_IMASK_MIFIRQ		0x00800000 /* mif needs attention */
#define GR_IMASK_HOSTTOTX	0x01000000 /* pkt moved from memory->tx fifo */
#define GR_IMASK_TXALL		0x02000000 /* all pkts in fifo transmitted */
#define GR_IMASK_TXEACK		0x04000000 /* error during tx dma */
#define GR_IMASK_TXLERR		0x08000000 /* late error during tx dma */
#define GR_IMASK_TXPERR		0x10000000 /* parity error during tx dma */
#define GR_IMASK_TXTERR		0x20000000 /* tag error during tx dma */
#define GR_IMASK_SLVERR		0x40000000 /* pio access error */
#define GR_IMASK_SLVPERR	0x80000000 /* PIO access parity error */

/*
 * external transmitter registers
 */
struct hme_txr {
	volatile u_int32_t	tx_pnding;	/* tx pending/wakeup */
        volatile u_int32_t	cfg;		/* tx cfg */
	volatile u_int32_t	tx_ring;	/* tx ring ptr */
	volatile u_int32_t	tx_bbase;	/* tx buffer base */
	volatile u_int32_t	tx_bdisp;	/* tx buffer displacement */
	volatile u_int32_t	tx_fifo_wptr;	/* tx fifo write pointer */
	volatile u_int32_t	tx_fifo_swptr;	/* tx fifo write ptr (shadow) */
	volatile u_int32_t	tx_fifo_rptr; 	/* tx fifo read pointer */
	volatile u_int32_t	tx_fifo_srptr;	/* tx fifo read ptr (shadow) */
	volatile u_int32_t	tx_fifo_pcnt;	/* tx fifo packet counter */
	volatile u_int32_t	smachine;	/* tx state machine */
	volatile u_int32_t	tx_rsize;	/* tx ring size */
	volatile u_int32_t	tx_bptr;	/* tx buffer pointer */
};

/* hme_txr.tx_pnding (tx pending/wakeup) */
#define TXR_TP_DMAWAKEUP	0x00000001	/* Restart transmit dma */

/* hme_txr.tx_cfg (tx configuration) */
#define TXR_CFG_DMAENABLE	0x00000001 /* enable tx dma */
#define TXR_CFG_FIFOTHRESH	0x000003fe /* tx fifo threshold */
#define TXR_CFG_IRQDAFTER	0x00000400 /* intr after tx-fifo empty */
#define TXR_CFG_IRQDBEFORE	0x00000000 /* intr before tx-fifo empty */
#define TXR_RSIZE_SHIFT		4

/*
 * external receiver registers
 */
struct hme_rxr {
	volatile u_int32_t	cfg;		/* rx cfg */
	volatile u_int32_t	rx_ring;	/* rx ring pointer */
	volatile u_int32_t	rx_bptr;	/* rx buffer ptr */
	volatile u_int32_t	rx_fifo_wptr;	/* rx fifo write ptr */
	volatile u_int32_t	rx_fifo_swptr;	/* rx fifo write ptr (shadow) */
	volatile u_int32_t	rx_fifo_rptr;	/* rx fifo read ptr */
	volatile u_int32_t	rx_fifo_srptr;	/* rx fifo read ptr (shadow) */
	volatile u_int32_t	smachine;	/* rx state machine */
};

/* hme_rxr.rx_cfg (rx configuration) */
#define RXR_CFG_DMAENABLE	0x00000001	/* rx dma enable */
#define RXR_CFG_reserved1	0x00000006	/* reserved bits */
#define RXR_CFG_BYTEOFFSET	0x00000038	/* rx first byte offset */
#define RXR_CFG_reserved2	0x000001c0	/* reserved bits */
#define RXR_CFG_RINGSIZE32	0x00000000	/* rx descptr ring size: 32 */
#define RXR_CFG_RINGSIZE64	0x00000200	/* rx descptr ring size: 64 */
#define RXR_CFG_RINGSIZE128	0x00000400	/* rx descptr ring size: 128 */
#define RXR_CFG_RINGSIZE256	0x00000600	/* rx descptr ring size: 128 */
#define RXR_CFG_reserved3	0x0000f800	/* reserved bits */
#define RXR_CFG_CSUMSTART	0x007f0000	/* rx offset of checksum */
#define RXR_CFG_CSUM_SHIFT	16

/*
 * configuration registers
 */
struct hme_cr {
        volatile u_int32_t	xif_cfg;	/* xif configuration reg */
        volatile u_int32_t	_padding[129];	/* reserved */
        volatile u_int32_t	tx_swreset;	/* tx software reset */
        volatile u_int32_t	tx_cfg;		/* tx configuration reg */
        volatile u_int32_t	ipkt_gap1;	/* interpacket gap 1 */
        volatile u_int32_t	ipkt_gap2;	/* interpacket gap 2 */
        volatile u_int32_t	attempt_limit;	/* tx attempt limit */
        volatile u_int32_t	stime;		/* tx slot time */
        volatile u_int32_t	preamble_len;	/* len of tx preamble */
        volatile u_int32_t	preamble_patt;	/* tx preamble pattern */
        volatile u_int32_t	tx_sframedelim;	/* tx frame delimiter */
        volatile u_int32_t	jsize;		/* tx jam size */
        volatile u_int32_t	tx_pkt_max;	/* tx maximum pkt size */
        volatile u_int32_t	tx_pkt_min;	/* tx minimum pkt size */
        volatile u_int32_t	peak_attempt;	/* tx peak counter */
        volatile u_int32_t	dt_ctr;		/* tx defer counter */
        volatile u_int32_t	nc_ctr;		/* tx normal collision cntr */
        volatile u_int32_t	fc_ctr;		/* tx first collision cntr */
        volatile u_int32_t	ex_ctr;		/* tx execess collision cntr */
        volatile u_int32_t	lt_ctr;		/* tx late collision cntr */
        volatile u_int32_t	rand_seed;	/* tx random seed */
        volatile u_int32_t	tx_smachine;	/* tx state machine */
        volatile u_int32_t	_padding2[44];	/* reserved */
        volatile u_int32_t	rx_swreset;	/* rx software reset */
        volatile u_int32_t	rx_cfg;		/* rx configuration */
        volatile u_int32_t	rx_pkt_max;	/* rx maximum pkt size */
        volatile u_int32_t	rx_pkt_min;	/* rx minimum pkt size */
        volatile u_int32_t	mac_addr2;	/* macaddress register2 (MSB) */
        volatile u_int32_t	mac_addr1;	/* macaddress register1 */
        volatile u_int32_t	mac_addr0;	/* macaddress register0 (LSB) */
        volatile u_int32_t	fr_ctr;		/* rx frame counter */
        volatile u_int32_t	gle_ctr;	/* rx giant counter */
        volatile u_int32_t	unale_ctr;	/* rx unaligned error cntr */
        volatile u_int32_t	rcrce_ctr;	/* rx crc error cntr */
        volatile u_int32_t	rx_smachine;	/* rx state machine */
        volatile u_int32_t	rx_cvalid;	/* rx code violation */
        volatile u_int32_t	_padding3;	/* reserved */
        volatile u_int32_t	htable3;	/* hash table 3 */
        volatile u_int32_t	htable2;	/* hash table 2 */
        volatile u_int32_t	htable1;	/* hash table 1 */
        volatile u_int32_t	htable0;	/* hash table 0 */
        volatile u_int32_t	afilter2;	/* address filter 2 */
        volatile u_int32_t	afilter1;	/* address filter 1 */
        volatile u_int32_t	afilter0;	/* address filter 0 */
        volatile u_int32_t	afilter_mask;	/* address filter mask */
};

/* BigMac XIF config register. */
#define CR_XCFG_ODENABLE  0x00000001 /* Output driver enable         */
#define CR_XCFG_XLBACK    0x00000002 /* Loopback-mode XIF enable     */
#define CR_XCFG_MLBACK    0x00000004 /* Loopback-mode MII enable     */
#define CR_XCFG_MIIDISAB  0x00000008 /* MII receive buffer disable   */
#define CR_XCFG_SQENABLE  0x00000010 /* SQE test enable              */
#define CR_XCFG_SQETWIN   0x000003e0 /* SQE time window              */
#define CR_XCFG_LANCE     0x00000010 /* Lance mode enable            */
#define CR_XCFG_LIPG0     0x000003e0 /* Lance mode IPG0              */

/* BigMac transmit config register. */
#define CR_TXCFG_ENABLE   0x00000001 /* Enable the transmitter       */
#define CR_TXCFG_SMODE    0x00000020 /* Enable slow transmit mode    */
#define CR_TXCFG_CIGN     0x00000040 /* Ignore transmit collisions   */
#define CR_TXCFG_FCSOFF   0x00000080 /* Do not emit FCS              */
#define CR_TXCFG_DBACKOFF 0x00000100 /* Disable backoff              */
#define CR_TXCFG_FULLDPLX 0x00000200 /* Enable full-duplex           */
#define CR_TXCFG_DGIVEUP  0x00000400 /* Don't give up on transmits   */

/* BigMac receive config register. */
#define CR_RXCFG_ENABLE   0x00000001 /* Enable the receiver             */
#define CR_RXCFG_PSTRIP   0x00000020 /* Pad byte strip enable           */
#define CR_RXCFG_PMISC    0x00000040 /* Enable promiscous mode          */
#define CR_RXCFG_DERR     0x00000080 /* Disable error checking          */
#define CR_RXCFG_DCRCS    0x00000100 /* Disable CRC stripping           */
#define CR_RXCFG_ME       0x00000200 /* Receive packets addressed to me */
#define CR_RXCFG_PGRP     0x00000400 /* Enable promisc group mode       */
#define CR_RXCFG_HENABLE  0x00000800 /* Enable the hash filter          */
#define CR_RXCFG_AENABLE  0x00001000 /* Enable the address filter       */

struct hme_tcvr {
	volatile u_int32_t bb_clock;	/* bit bang clock */
	volatile u_int32_t bb_data;	/* bit bang data */
	volatile u_int32_t bb_oenab;	/* bit bang output enable */
	volatile u_int32_t frame;	/* frame control & data */
	volatile u_int32_t cfg;		/* MIF configuration */
	volatile u_int32_t int_mask;	/* MIF interrupt mask */
	volatile u_int32_t status;	/* MIF status */
	volatile u_int32_t smachine;	/* MIF state machine */
};

#define	FRAME_WRITE	0x50020000	/* start a frame write */
#define	FRAME_READ	0x60020000	/* start a frame read */
#define	TCVR_FAILURE	0x80000000	/* impossible value */

/* Transceiver config register */
#define TCVR_CFG_PSELECT	    0x00000001	/* select PHY */
#define TCVR_CFG_PENABLE	0x00000002	/* enable MIF polling */
#define TCVR_CFG_BENABLE	0x00000004	/* enable bit bang */
#define TCVR_CFG_PREGADDR	0x000000f8	/* poll register addr */
#define TCVR_CFG_MDIO0		0x00000100	/* MDIO zero, data/attached */
#define TCVR_CFG_MDIO1		0x00000200	/* MDIO one,  data/attached */
#define TCVR_CFG_PDADDR		0x00007c00	/* device phy addr polling */

/* Here are some PHY addresses. */
#define	TCVR_PHYADDR_ETX		0	/* external transceiver */
#define	TCVR_PHYADDR_ITX		1	/* internal transceiver */

/* Transceiver status register */
#define TCVR_STAT_BASIC		0xffff0000	/* The "basic" part */
#define TCVR_STAT_NORMAL	0x0000ffff	/* The "non-basic" part */

/* hme flags */
#define	HME_FLAG_POLL		0x00000001	/* polling mif? */
#define	HME_FLAG_FENABLE	0x00000002	/* MII frame enabled? */
#define	HME_FLAG_LANCE		0x00000004	/* Lance mode IPG0? */
#define	HME_FLAG_RXENABLE	0x00000008	/* Receiver enabled? */
#define	HME_FLAG_AUTO		0x00000010	/* Auto-Neg? 0 = force */
#define	HME_FLAG_FULL		0x00000020	/* Full duplex enabled? */
#define	HME_FLAG_MACFULL	0x00000040	/* Full duplex in the MAC? */
#define	HME_FLAG_POLLENABLE	0x00000080	/* Try MIF polling? */
#define	HME_FLAG_RXCV		0x00000100	/* RXCV enable - XXX */
#define	HME_FLAG_INIT		0x00000200	/* Initialized once? */
#define	HME_FLAG_LINKUP		0x00000400	/* Is link up? */

#define	HME_FLAG_20_21		\
    (HME_FLAG_POLLENABLE | HME_FLAG_FENABLE)
#define HME_FLAG_NOT_A0 \
    (HME_FLAG_POLLENABLE | HME_FLAG_FENABLE | HME_FLAG_LANCE | HME_FLAG_RXCV)

/*
 * Transceiver type
 */
#define HME_TCVR_EXTERNAL	0
#define HME_TCVR_INTERNAL	1
#define HME_TCVR_NONE		2

struct hme_rxd {
	volatile u_int32_t	rx_flags;
	volatile u_int32_t	rx_addr;
};
#define	HME_RXD_OWN		0x80000000	/* desc owner: 1=hw,0=sw */
#define HME_RXD_OVERFLOW	0x40000000	/* 1 = buffer over flow */
#define HME_RXD_SIZE		0x3fff0000	/* descriptor size */
#define HME_RXD_CSUM		0x0000ffff	/* checksum mask */

struct hme_txd {
	volatile u_int32_t	tx_flags;
	volatile u_int32_t	tx_addr;
};
#define	HME_TXD_OWN		0x80000000	/* desc owner: 1=hw,0=sw */
#define	HME_TXD_SOP		0x40000000 	/* 1 = start of pkt */
#define	HME_TXD_EOP		0x20000000	/* 1 = end of pkt */
#define	HME_TXD_CSENABLE	0x10000000	/* 1 = use hw checksums */
#define	HME_TXD_CSLOCATION	0x0ff00000	/* checksum location mask */
#define	HME_TXD_CSBUFBEGIN	0x000fc000	/* checksum begin mask */
#define	HME_TXD_SIZE		0x00003fff 	/* pkt size mask */

#define HME_RX_RING_SIZE	32		/* Must be 32,64,128, or 256 */
#define HME_TX_RING_SIZE	32		/* 16<=x<=256 and div by 16 */
#define HME_RX_RING_MAX		256		/* maximum ring size: rx */
#define HME_TX_RING_MAX		256		/* maximum ring size: tx */
#define HME_RX_PKT_BUF_SZ	2048		/* size of a rx buffer */
#define HME_RX_OFFSET		2		/* packet offset */
#define HME_RX_CSUMLOC		0x00		/* checksum location */
#define HME_TX_PKT_BUF_SZ	1546		/* size of a tx buffer */
#define HME_RX_ALIGN_SIZE	64		/* XXX rx bufs must align 64 */
#define HME_RX_ALIGN_MASK	(~(RX_ALIGN_SIZE - 1))

struct hme_desc {
	struct hme_rxd hme_rxd[HME_RX_RING_MAX];
	struct hme_txd hme_txd[HME_TX_RING_MAX];
};

struct hme_bufs {
	char rx_buf[HME_RX_RING_SIZE][HME_RX_PKT_BUF_SZ];
	char tx_buf[HME_TX_RING_SIZE][HME_TX_PKT_BUF_SZ];
};

license: unknown


code:
/*	$OpenBSD: hmevar.h,v 1.14 2009/07/16 07:18:47 sthen Exp $	*/

/*
 * Copyright (c) 1998 Jason L. Wright (jason@thought.net)
 * 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, 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 ``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 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.
 */

struct hme_softc {
	struct	device sc_dev;		/* base device */
	struct	intrhand sc_ih;		/* interrupt vectoring */
	int	sc_node;		/* which sbus node */

	mii_data_t	sc_mii;		/* mii bus */
	struct timeout	sc_tick;	/* tick timeout */

	struct	arpcom sc_arpcom;	/* ethernet common */

	/*
	 * Register sets
	 */
	struct	hme_gr *sc_gr;		/* global registers */
	struct	hme_txr *sc_txr;	/* transmitter regs */
	struct	hme_rxr *sc_rxr;	/* receiver registers */
	struct	hme_cr *sc_cr;		/* configuration registers */
	struct	hme_tcvr *sc_tcvr;	/* MIF registers */

	int	sc_burst;		/* DMA burst size in effect */
	int	sc_rev;			/* Card revision */

	u_int32_t	sc_flags;	/* status flags	*/

	/*
	 * RX/TX ring buffers, descriptors, and counters
	 */
	struct	hme_desc *sc_desc, *sc_desc_dva;	/* ring descriptors */
	struct	hme_bufs *sc_bufs, *sc_bufs_dva;	/* packet buffers */
	int	sc_first_td, sc_last_td, sc_no_td;	/* tx counters */
	int	sc_last_rd;				/* rx counters */
};


code:
/*	$OpenBSD: i82586.h,v 1.2 1997/08/08 08:25:07 downsj Exp $	*/
/*	$NetBSD: i82586.h,v 1.3 1995/01/27 09:49:55 pk Exp $ */

/*-
 * Copyright (c) 1992, University of Vermont and State Agricultural College.
 * Copyright (c) 1992, Garrett A. Wollman.
 * 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, 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	Vermont and State Agricultural College and Garrett A. Wollman.
 * 4. Neither the name of the University nor the name of the author
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHOR 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.
 */

/*
 * Intel 82586 Ethernet chip
 * Register, bit, and structure definitions.
 *
 * Written by GAW with reference to the Clarkson Packet Driver code for this
 * chip written by Russ Nelson and others.
 *
 * Sun support added by Charles D. Cranor, 25-Oct-94
 */

struct ie_en_addr {
	u_char data[6];
};

/*
 * This is the master configuration block.  It tells the hardware where all
 * the rest of the stuff is.
 */
struct ie_sys_conf_ptr {
	u_short mbz;			/* must be zero */
	u_char ie_bus_use;		/* true if 8-bit only */
	u_char mbz2[5];			/* must be zero */
	caddr_t ie_iscp_ptr;		/* 24-bit physaddr of ISCP */
};

/*
 * Note that this is wired in hardware; the SCP is always located here, no
 * matter what.
 */
#define IE_SCP_ADDR 0xfffff4

/*
 * The tells the hardware where all the rest of the stuff is, too.
 * FIXME: some of these should be re-commented after we figure out their
 * REAL function.
 */
struct ie_int_sys_conf_ptr {
	u_char ie_busy;			/* zeroed after init */
	u_char mbz;
	u_short ie_scb_offset;		/* 16-bit physaddr of next struct */
	caddr_t ie_base;		/* 24-bit physaddr for all 16-bit vars */
};

/*
 * This FINALLY tells the hardware what to do and where to put it.
 */
struct ie_sys_ctl_block {
	u_short ie_status;		/* status word */
	u_short ie_command;		/* command word */
	u_short ie_command_list;	/* 16-pointer to command block list */
	u_short ie_recv_list;		/* 16-pointer to receive frame list */
	u_short ie_err_crc;		/* CRC errors */
	u_short ie_err_align;		/* Alignment errors */
	u_short ie_err_resource;	/* Resource errors */
	u_short ie_err_overrun;		/* Overrun errors */
};

/* Command values */
#define IE_RU_COMMAND	SWAP(0x0070)	/* mask for RU command */
#define IE_RU_NOP	SWAP(0) 	/* for completeness */
#define IE_RU_START	SWAP(0x0010)	/* start receive unit command */
#define IE_RU_ENABLE	SWAP(0x0020)	/* enable receiver command */
#define IE_RU_DISABLE	SWAP(0x0030)	/* disable receiver command */
#define IE_RU_ABORT	SWAP(0x0040)	/* abort current receive operation */

#define IE_CU_COMMAND	SWAP(0x0700)	/* mask for CU command */
#define IE_CU_NOP	SWAP(0) 	/* included for completeness */
#define IE_CU_START	SWAP(0x0100)	/* do-command command */
#define IE_CU_RESUME	SWAP(0x0200)	/* resume a suspended cmd list */
#define IE_CU_STOP	SWAP(0x0300)	/* SUSPEND was already taken */
#define IE_CU_ABORT	SWAP(0x0400)	/* abort current command */

#define IE_ACK_COMMAND	SWAP(0xf000)	/* mask for ACK command */
#define IE_ACK_CX	SWAP(0x8000)	/* ack IE_ST_DONE */
#define IE_ACK_FR	SWAP(0x4000)	/* ack IE_ST_RECV */
#define IE_ACK_CNA	SWAP(0x2000)	/* ack IE_ST_ALLDONE */
#define IE_ACK_RNR	SWAP(0x1000)	/* ack IE_ST_RNR */

#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START)
				/* is this command an action command? */

/* Status values */
#define IE_ST_WHENCE	SWAP(0xf000)	/* mask for cause of interrupt */
#define IE_ST_DONE	SWAP(0x8000)	/* command with I bit completed */
#define IE_ST_RECV	SWAP(0x4000)	/* frame received */
#define IE_ST_ALLDONE	SWAP(0x2000)	/* all commands completed */
#define IE_ST_RNR	SWAP(0x1000)	/* receive not ready */

#define IE_CU_STATUS	SWAP(0x700)	/* mask for command unit status */
#define IE_CU_ACTIVE	SWAP(0x200)	/* command unit is active */
#define IE_CU_SUSPEND	SWAP(0x100)	/* command unit is suspended */

#define IE_RU_STATUS	SWAP(0x70)	/* mask for receiver unit status */
#define IE_RU_SUSPEND	SWAP(0x10)	/* receiver is suspended */
#define IE_RU_NOSPACE	SWAP(0x20)	/* receiver has no resources */
#define IE_RU_READY	SWAP(0x40)	/* reveiver is ready */

/*
 * This is filled in partially by the chip, partially by us.
 */
struct ie_recv_frame_desc {
	u_short ie_fd_status;		/* status for this frame */
	u_short ie_fd_last;		/* end of frame list flag */
	u_short ie_fd_next;		/* 16-pointer to next RFD */
	u_short ie_fd_buf_desc;		/* 16-pointer to list of buffer desc's */
	struct ie_en_addr dest;		/* destination ether */
	struct ie_en_addr src;		/* source ether */
	u_short ie_length;		/* 802 length/Ether type */
	u_short mbz;			/* must be zero */
};

#define IE_FD_LAST	SWAP(0x8000)	/* last rfd in list */
#define IE_FD_SUSP	SWAP(0x4000)	/* suspend RU after receipt */

#define IE_FD_COMPLETE	SWAP(0x8000)	/* frame is complete */
#define IE_FD_BUSY	SWAP(0x4000)	/* frame is busy */
#define IE_FD_OK	SWAP(0x2000)     /* frame is bad */
#define IE_FD_RNR	SWAP(0x0200)	/* receiver out of resources here */

/*
 * linked list of buffers...
 */
struct ie_recv_buf_desc {
	u_short ie_rbd_actual;		/* status for this buffer */
	u_short ie_rbd_next;		/* 16-pointer to next RBD */
	caddr_t ie_rbd_buffer;		/* 24-pointer to buffer for this RBD */
	u_short ie_rbd_length;		/* length of the buffer */
	u_short mbz;			/* must be zero */
};

#define IE_RBD_LAST	SWAP(0x8000)	/* last buffer */
#define IE_RBD_USED	SWAP(0x4000)	/* this buffer has data */
/*
 * All commands share this in common.
 */
struct ie_cmd_common {
	u_short ie_cmd_status;		/* status of this command */
	u_short ie_cmd_cmd;		/* command word */
	u_short ie_cmd_link;		/* link to next command */
};

#define IE_STAT_COMPL	SWAP(0x8000)	/* command is completed */
#define IE_STAT_BUSY	SWAP(0x4000)	/* command is running now */
#define IE_STAT_OK	SWAP(0x2000)	/* command completed successfully */
#define IE_STAT_ABORT	SWAP(0x1000)	/* command was aborted */

#define IE_CMD_NOP	SWAP(0x0000)	/* NOP */
#define IE_CMD_IASETUP	SWAP(0x0001)	/* initial address setup */
#define IE_CMD_CONFIG	SWAP(0x0002)	/* configure command */
#define IE_CMD_MCAST	SWAP(0x0003)	/* multicast setup command */
#define IE_CMD_XMIT	SWAP(0x0004)	/* transmit command */
#define IE_CMD_TDR	SWAP(0x0005)	/* time-domain reflectometer command */
#define IE_CMD_DUMP	SWAP(0x0006)	/* dump command */
#define IE_CMD_DIAGNOSE	SWAP(0x0007)	/* diagnostics command */

#define IE_CMD_LAST	SWAP(0x8000)	/* this is the last command in the list */
#define IE_CMD_SUSPEND	SWAP(0x4000)	/* suspend CU after this command */
#define IE_CMD_INTR	SWAP(0x2000)	/* post an interrupt after completion */

/*
 * This is the command to transmit a frame.
 */
struct ie_xmit_cmd {
	struct ie_cmd_common com;	/* common part */
#define ie_xmit_status com.ie_cmd_status

	u_short ie_xmit_desc;		/* 16-pointer to buffer descriptor */
	struct ie_en_addr ie_xmit_addr; /* destination address */

	u_short ie_xmit_length;		/* 802.3 length/Ether type field */
};

#define IE_XS_MAXCOLL  	SWAP(0x000f)	/* number of collisions during transmit */
#define IE_XS_EXCMAX	SWAP(0x0020)	/* exceeded maximum number of collisions */
#define IE_XS_SQE	SWAP(0x0040)	/* SQE positive */
#define IE_XS_DEFERRED	SWAP(0x0080)	/* transmission deferred */
#define IE_XS_UNDERRUN	SWAP(0x0100)	/* DMA underrun */
#define IE_XS_LOSTCTS	SWAP(0x0200)	/* Lost CTS */
#define IE_XS_NOCARRIER	SWAP(0x0400)	/* No Carrier */

/*
 * This is a buffer descriptor for a frame to be transmitted.
 */

struct ie_xmit_buf {
	u_short ie_xmit_flags;		/* see below */
	u_short ie_xmit_next;		/* 16-pointer to next desc. */
	caddr_t ie_xmit_buf;		/* 24-pointer to the actual buffer */
};

#define IE_XMIT_LAST SWAP(0x8000)	/* this TBD is the last one */
/* The rest of the `flags' word is actually the length. */

/*
 * Multicast setup command.
 */

#define MAXMCAST 250		/* must fit in transmit buffer */

struct ie_mcast_cmd {
	struct ie_cmd_common com;	/* common part */
#define ie_mcast_status com.ie_cmd_status

	u_short ie_mcast_bytes;	/* size (in bytes) of multicast addresses */
	struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1];	/* space for them */
};

/*
 * Time Domain Reflectometer command.
 */

struct ie_tdr_cmd {
	struct ie_cmd_common com;	/* common part */
#define ie_tdr_status com.ie_cmd_status

	u_short ie_tdr_time;		/* error bits and time */
};

#define IE_TDR_SUCCESS	SWAP(0x8000)	/* TDR succeeded without error */
#define IE_TDR_XCVR	SWAP(0x4000)	/* detected a transceiver problem */
#define IE_TDR_OPEN	SWAP(0x2000)	/* detected an open */
#define IE_TDR_SHORT	SWAP(0x1000)	/* TDR detected a short */
#define IE_TDR_TIME	SWAP(0x07ff)	/* mask for reflection time */

/*
 * Initial Address Setup command
 */
struct ie_iasetup_cmd {
	struct ie_cmd_common com;
#define ie_iasetup_status com.ie_cmd_status

	struct ie_en_addr ie_address;
};

/*
 * Configuration command
 */
struct ie_config_cmd {
	struct ie_cmd_common com;	/* common part */
#define ie_config_status com.ie_cmd_status

	u_char ie_config_count;		/* byte count (0x0c) */
	u_char ie_fifo;			/* fifo (8) */
	u_char ie_save_bad;		/* save bad frames (0x40) */
	u_char ie_addr_len;		/* address length (0x2e) (AL-LOC == 1) */
	u_char ie_priority;		/* priority and backoff (0x0) */
	u_char ie_ifs;			/* inter-frame spacing (0x60) */
	u_char ie_slot_low;		/* slot time, LSB (0x0) */
	u_char ie_slot_high;		/* slot time, MSN, and retries (0xf2) */
	u_char ie_promisc;		/* 1 if promiscuous, else 0 */
	u_char ie_crs_cdt;		/* CSMA/CD parameters (0x0) */
	u_char ie_min_len;		/* min frame length (0x40) */
	u_char ie_junk;			/* stuff for 82596 (0xff) */
};


code:
/*	$OpenBSD: if_gem_sbus.c,v 1.4 2015/03/28 19:07:07 miod Exp $	*/
/*	$NetBSD: if_gem_sbus.c,v 1.1 2006/11/24 13:23:32 martin Exp $	*/

/*-
 * Copyright (c) 2006 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Martin Husemann.
 *
 * 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, 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

/*
 * SBus front-end for the GEM network driver
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/socket.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
extern struct sparc_bus_dma_tag *iommu_dmatag;

#include <dev/ic/gemreg.h>
#include <dev/ic/gemvar.h>

#include <dev/ofw/openfirm.h>

struct gem_sbus_softc {
	struct	gem_softc	gsc_gem;	/* GEM device */
	struct intrhand		gsc_ih;
	struct rom_reg		gsc_rr;
};

int	gemmatch_sbus(struct device *, void *, void *);
void	gemattach_sbus(struct device *, struct device *, void *);

struct cfattach gem_sbus_ca = {
	sizeof(struct gem_sbus_softc), gemmatch_sbus, gemattach_sbus
};

int
gemmatch_sbus(struct device *parent, void *vcf, void *aux)
{
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	return (strcmp("network", ra->ra_name) == 0);
}

void
gemattach_sbus(struct device *parent, struct device *self, void *aux)
{
	struct confargs *ca = aux;
	struct gem_sbus_softc *gsc = (void *)self;
	struct gem_softc *sc = &gsc->gsc_gem;
	/* XXX the following declaration should be elsewhere */
	extern void myetheraddr(u_char *);

	/* Pass on the bus tags */
	gsc->gsc_rr = ca->ca_ra.ra_reg[0];
	sc->sc_bustag = &gsc->gsc_rr;
	sc->sc_dmatag = iommu_dmatag;

	if (ca->ca_ra.ra_nintr < 1) {
                printf(": no interrupt\n");
                return;
        }

	if (ca->ca_ra.ra_nreg < 2) {
                printf(": only %d register sets\n", ca->ca_ra.ra_nreg);
		return;
	}

	sc->sc_variant = GEM_SUN_GEM;

	/*
	 * Map two register banks:
	 *
	 *	bank 0: status, config, reset
	 *	bank 1: various gem parts
	 *
	 */
	if (bus_space_map(&ca->ca_ra.ra_reg[0], 0,
	    ca->ca_ra.ra_reg[0].rr_len, 0, &sc->sc_h2)) {
		printf(": can't map registers\n");
		return;
	}
	if (bus_space_map(&ca->ca_ra.ra_reg[1], 0,
	    ca->ca_ra.ra_reg[1].rr_len, 0, &sc->sc_h1)) {
		printf(": can't map registers\n");
		return;
	}

	if (getprop(ca->ca_ra.ra_node, "local-mac-address",
	    sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN) <= 0)
		myetheraddr(sc->sc_arpcom.ac_enaddr);

	/*
	 * SBUS config
	 */
	(void) bus_space_read_4(sc->sc_bustag, sc->sc_h2, GEM_SBUS_RESET);
	delay(100);
	bus_space_write_4(sc->sc_bustag, sc->sc_h2, GEM_SBUS_CONFIG,
	    GEM_SBUS_CFG_BSIZE32);

	/* Establish interrupt handler */
	gsc->gsc_ih.ih_fun = gem_intr;
	gsc->gsc_ih.ih_arg = sc;
	intr_establish(ca->ca_ra.ra_intr[0].int_pri, &gsc->gsc_ih,
	    IPL_NET, self->dv_xname);

	gem_config(sc);
}