summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2004-08-10 07:30:58 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2004-08-10 07:30:58 +0000
commitff15eee627d66e8dbd2c70669b6afa904644af41 (patch)
tree34106e522587be93c91bd8ed3a92b6427261468f
parentb69d8cf2c83f155888ad9003d5197f14a0cfe646 (diff)
mac-110 on mace from netbsd
-rw-r--r--sys/arch/sgi/conf/GENERIC3
-rw-r--r--sys/arch/sgi/conf/files.sgi7
-rw-r--r--sys/arch/sgi/dev/if_mec.c1505
-rw-r--r--sys/arch/sgi/dev/if_mecreg.h151
4 files changed, 1664 insertions, 2 deletions
diff --git a/sys/arch/sgi/conf/GENERIC b/sys/arch/sgi/conf/GENERIC
index 615907237b2..d9f7465ac95 100644
--- a/sys/arch/sgi/conf/GENERIC
+++ b/sys/arch/sgi/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.3 2004/08/09 14:57:26 pefo Exp $
+# $OpenBSD: GENERIC,v 1.4 2004/08/10 07:30:56 mickey Exp $
#
machine sgi mips64
@@ -48,6 +48,7 @@ macebus0 at mainbus0 # MACE controller localbus.
# Localbus devices
clock0 at macebus0
+#mec0 at macebus0 sys 0x18 base 0x00280000 irq 4
com0 at macebus0 sys 0x18 base 0x00390000 irq 5
com1 at macebus0 sys 0x18 base 0x00398000 irq 5
diff --git a/sys/arch/sgi/conf/files.sgi b/sys/arch/sgi/conf/files.sgi
index 700dbdd4753..23efacfd89b 100644
--- a/sys/arch/sgi/conf/files.sgi
+++ b/sys/arch/sgi/conf/files.sgi
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sgi,v 1.1 2004/08/06 21:12:18 pefo Exp $
+# $OpenBSD: files.sgi,v 1.2 2004/08/10 07:30:56 mickey Exp $
#
# maxpartitions must be first item in files.${ARCH}
#
@@ -75,6 +75,11 @@ file arch/sgi/localbus/com_lbus.c com_localbus
attach lpt at macebus with lpt_localbus
file arch/sgi/dev/lpt_lbus.c lpt_localbus
+# MACE MAC-110 ethernet
+device mec: ether, ifnet, ifmedia, mii
+attach mec at macebus
+file arch/sgi/dev/if_mec.c mec
+
# Raster operations
include "dev/rasops/files.rasops"
include "dev/wsfont/files.wsfont"
diff --git a/sys/arch/sgi/dev/if_mec.c b/sys/arch/sgi/dev/if_mec.c
new file mode 100644
index 00000000000..3db7d5a2992
--- /dev/null
+++ b/sys/arch/sgi/dev/if_mec.c
@@ -0,0 +1,1505 @@
+/* $OpenBSD: if_mec.c,v 1.1 2004/08/10 07:30:57 mickey Exp $ */
+/* $NetBSD: if_mec_mace.c,v 1.5 2004/08/01 06:36:36 tsutsui Exp $ */
+
+/*
+ * Copyright (c) 2004 Izumi Tsutsui.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2003 Christopher SEKIYA
+ * 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 for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*
+ * MACE MAC-110 ethernet driver
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <netinet/if_ether.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/autoconf.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <mips64/archtype.h>
+#include <mips64/arcbios.h>
+#include <sgi/dev/if_mecreg.h>
+
+#define MEC_DEBUG
+
+#ifdef MEC_DEBUG
+#define MEC_DEBUG_RESET 0x01
+#define MEC_DEBUG_START 0x02
+#define MEC_DEBUG_STOP 0x04
+#define MEC_DEBUG_INTR 0x08
+#define MEC_DEBUG_RXINTR 0x10
+#define MEC_DEBUG_TXINTR 0x20
+uint32_t mec_debug = 0xff;
+#define DPRINTF(x, y) if (mec_debug & (x)) printf y
+#else
+#define DPRINTF(x, y) /* nothing */
+#endif
+
+/*
+ * Transmit descriptor list size
+ */
+#define MEC_NTXDESC 64
+#define MEC_NTXDESC_MASK (MEC_NTXDESC - 1)
+#define MEC_NEXTTX(x) (((x) + 1) & MEC_NTXDESC_MASK)
+
+/*
+ * software state for TX
+ */
+struct mec_txsoft {
+ struct mbuf *txs_mbuf; /* head of our mbuf chain */
+ bus_dmamap_t txs_dmamap; /* our DMA map */
+ uint32_t txs_flags;
+#define MEC_TXS_BUFLEN_MASK 0x0000007f /* data len in txd_buf */
+#define MEC_TXS_TXDBUF 0x00000080 /* txd_buf is used */
+#define MEC_TXS_TXDPTR1 0x00000100 /* txd_ptr[0] is used */
+};
+
+/*
+ * Transmit buffer descriptor
+ */
+#define MEC_TXDESCSIZE 128
+#define MEC_NTXPTR 3
+#define MEC_TXD_BUFOFFSET \
+ (sizeof(uint64_t) + MEC_NTXPTR * sizeof(uint64_t))
+#define MEC_TXD_BUFSIZE (MEC_TXDESCSIZE - MEC_TXD_BUFOFFSET)
+#define MEC_TXD_BUFSTART(len) (MEC_TXD_BUFSIZE - (len))
+#define MEC_TXD_ALIGN 8
+#define MEC_TXD_ROUNDUP(addr) \
+ (((addr) + (MEC_TXD_ALIGN - 1)) & ~((uint64_t)MEC_TXD_ALIGN - 1))
+
+struct mec_txdesc {
+ volatile uint64_t txd_cmd;
+#define MEC_TXCMD_DATALEN 0x000000000000ffff /* data length */
+#define MEC_TXCMD_BUFSTART 0x00000000007f0000 /* start byte offset */
+#define TXCMD_BUFSTART(x) ((x) << 16)
+#define MEC_TXCMD_TERMDMA 0x0000000000800000 /* stop DMA on abort */
+#define MEC_TXCMD_TXINT 0x0000000001000000 /* INT after TX done */
+#define MEC_TXCMD_PTR1 0x0000000002000000 /* valid 1st txd_ptr */
+#define MEC_TXCMD_PTR2 0x0000000004000000 /* valid 2nd txd_ptr */
+#define MEC_TXCMD_PTR3 0x0000000008000000 /* valid 3rd txd_ptr */
+#define MEC_TXCMD_UNUSED 0xfffffffff0000000ULL /* should be zero */
+
+#define txd_stat txd_cmd
+#define MEC_TXSTAT_LEN 0x000000000000ffff /* TX length */
+#define MEC_TXSTAT_COLCNT 0x00000000000f0000 /* collision count */
+#define MEC_TXSTAT_COLCNT_SHIFT 16
+#define MEC_TXSTAT_LATE_COL 0x0000000000100000 /* late collision */
+#define MEC_TXSTAT_CRCERROR 0x0000000000200000 /* */
+#define MEC_TXSTAT_DEFERRED 0x0000000000400000 /* */
+#define MEC_TXSTAT_SUCCESS 0x0000000000800000 /* TX complete */
+#define MEC_TXSTAT_TOOBIG 0x0000000001000000 /* */
+#define MEC_TXSTAT_UNDERRUN 0x0000000002000000 /* */
+#define MEC_TXSTAT_COLLISIONS 0x0000000004000000 /* */
+#define MEC_TXSTAT_EXDEFERRAL 0x0000000008000000 /* */
+#define MEC_TXSTAT_COLLIDED 0x0000000010000000 /* */
+#define MEC_TXSTAT_UNUSED 0x7fffffffe0000000ULL /* should be zero */
+#define MEC_TXSTAT_SENT 0x8000000000000000ULL /* packet sent */
+
+ uint64_t txd_ptr[MEC_NTXPTR];
+#define MEC_TXPTR_UNUSED2 0x0000000000000007 /* should be zero */
+#define MEC_TXPTR_DMAADDR 0x00000000fffffff8 /* TX DMA address */
+#define MEC_TXPTR_LEN 0x0000ffff00000000ULL /* buffer length */
+#define TXPTR_LEN(x) ((uint64_t)(x) << 32)
+#define MEC_TXPTR_UNUSED1 0xffff000000000000ULL /* should be zero */
+
+ uint8_t txd_buf[MEC_TXD_BUFSIZE];
+};
+
+/*
+ * Receive buffer size
+ */
+#define MEC_NRXDESC 16
+#define MEC_NRXDESC_MASK (MEC_NRXDESC - 1)
+#define MEC_NEXTRX(x) (((x) + 1) & MEC_NRXDESC_MASK)
+
+/*
+ * Receive buffer description
+ */
+#define MEC_RXDESCSIZE 4096 /* umm, should be 4kbyte aligned */
+#define MEC_RXD_NRXPAD 3
+#define MEC_RXD_DMAOFFSET (1 + MEC_RXD_NRXPAD)
+#define MEC_RXD_BUFOFFSET (MEC_RXD_DMAOFFSET * sizeof(uint64_t))
+#define MEC_RXD_BUFSIZE (MEC_RXDESCSIZE - MEC_RXD_BUFOFFSET)
+
+struct mec_rxdesc {
+ volatile uint64_t rxd_stat;
+#define MEC_RXSTAT_LEN 0x000000000000ffff /* data length */
+#define MEC_RXSTAT_VIOLATION 0x0000000000010000 /* code violation (?) */
+#define MEC_RXSTAT_UNUSED2 0x0000000000020000 /* unknown (?) */
+#define MEC_RXSTAT_CRCERROR 0x0000000000040000 /* CRC error */
+#define MEC_RXSTAT_MULTICAST 0x0000000000080000 /* multicast packet */
+#define MEC_RXSTAT_BROADCAST 0x0000000000100000 /* broadcast packet */
+#define MEC_RXSTAT_INVALID 0x0000000000200000 /* invalid preamble */
+#define MEC_RXSTAT_LONGEVENT 0x0000000000400000 /* long packet */
+#define MEC_RXSTAT_BADPACKET 0x0000000000800000 /* bad packet */
+#define MEC_RXSTAT_CAREVENT 0x0000000001000000 /* carrier event */
+#define MEC_RXSTAT_MATCHMCAST 0x0000000002000000 /* match multicast */
+#define MEC_RXSTAT_MATCHMAC 0x0000000004000000 /* match MAC */
+#define MEC_RXSTAT_SEQNUM 0x00000000f8000000 /* sequence number */
+#define MEC_RXSTAT_CKSUM 0x0000ffff00000000ULL /* IP checksum */
+#define MEC_RXSTAT_UNUSED1 0x7fff000000000000ULL /* should be zero */
+#define MEC_RXSTAT_RECEIVED 0x8000000000000000ULL /* set to 1 on RX */
+ uint64_t rxd_pad1[MEC_RXD_NRXPAD];
+ uint8_t rxd_buf[MEC_RXD_BUFSIZE];
+};
+
+/*
+ * control structures for DMA ops
+ */
+struct mec_control_data {
+ /*
+ * TX descriptors and buffers
+ */
+ struct mec_txdesc mcd_txdesc[MEC_NTXDESC];
+
+ /*
+ * RX descriptors and buffers
+ */
+ struct mec_rxdesc mcd_rxdesc[MEC_NRXDESC];
+};
+
+/*
+ * It _seems_ there are some restrictions on descriptor address:
+ *
+ * - Base address of txdescs should be 8kbyte aligned
+ * - Each txdesc should be 128byte aligned
+ * - Each rxdesc should be 4kbyte aligned
+ *
+ * So we should specify 64k align to allocalte txdescs.
+ * In this case, sizeof(struct mec_txdesc) * MEC_NTXDESC is 8192
+ * so rxdescs are also allocated at 4kbyte aligned.
+ */
+#define MEC_CONTROL_DATA_ALIGN (8 * 1024)
+
+#define MEC_CDOFF(x) offsetof(struct mec_control_data, x)
+#define MEC_CDTXOFF(x) MEC_CDOFF(mcd_txdesc[(x)])
+#define MEC_CDRXOFF(x) MEC_CDOFF(mcd_rxdesc[(x)])
+
+/*
+ * software state per device
+ */
+struct mec_softc {
+ struct device sc_dev; /* generic device structures */
+ struct arpcom sc_ac; /* Ethernet common part */
+
+ bus_space_tag_t sc_st; /* bus_space tag */
+ bus_space_handle_t sc_sh; /* bus_space handle */
+ bus_dma_tag_t sc_dmat; /* bus_dma tag */
+ void *sc_sdhook; /* shoutdown hook */
+
+ struct mii_data sc_mii; /* MII/media information */
+ int sc_phyaddr; /* MII address */
+ struct timeout sc_tick_ch; /* tick timeout */
+
+ bus_dmamap_t sc_cddmamap; /* bus_dma map for control data */
+#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr
+
+ /* pointer to allocalted control data */
+ struct mec_control_data *sc_control_data;
+#define sc_txdesc sc_control_data->mcd_txdesc
+#define sc_rxdesc sc_control_data->mcd_rxdesc
+
+ /* software state for TX descs */
+ struct mec_txsoft sc_txsoft[MEC_NTXDESC];
+
+ int sc_txpending; /* number of TX requests pending */
+ int sc_txdirty; /* first dirty TX descriptor */
+ int sc_txlast; /* last used TX descriptor */
+
+ int sc_rxptr; /* next ready RX buffer */
+};
+
+#define MEC_CDTXADDR(sc, x) ((sc)->sc_cddma + MEC_CDTXOFF(x))
+#define MEC_CDRXADDR(sc, x) ((sc)->sc_cddma + MEC_CDRXOFF(x))
+
+#define MEC_TXDESCSYNC(sc, x, ops) \
+ bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \
+ MEC_CDTXOFF(x), MEC_TXDESCSIZE, (ops))
+#define MEC_TXCMDSYNC(sc, x, ops) \
+ bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \
+ MEC_CDTXOFF(x), sizeof(uint64_t), (ops))
+
+#define MEC_RXSTATSYNC(sc, x, ops) \
+ bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \
+ MEC_CDRXOFF(x), sizeof(uint64_t), (ops))
+#define MEC_RXBUFSYNC(sc, x, len, ops) \
+ bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \
+ MEC_CDRXOFF(x) + MEC_RXD_BUFOFFSET, \
+ ETHER_ALIGN + (len), (ops))
+
+/* XXX these values should be moved to <net/if_ether.h> ? */
+#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN)
+
+struct cfdriver mec_cd = {
+ NULL, "mec", DV_IFNET
+};
+
+int mec_match(struct device *, void *, void *);
+void mec_attach(struct device *, struct device *, void *);
+
+struct cfattach mec_ca = {
+ sizeof(struct mec_softc), mec_match, mec_attach
+};
+
+int mec_mii_readreg(struct device *, int, int);
+void mec_mii_writereg(struct device *, int, int, int);
+int mec_mii_wait(struct mec_softc *);
+void mec_statchg(struct device *);
+void mec_mediastatus(struct ifnet *, struct ifmediareq *);
+int mec_mediachange(struct ifnet *);
+
+static void enaddr_aton(const char *, uint8_t *);
+
+int mec_init(struct ifnet * ifp);
+void mec_start(struct ifnet *);
+void mec_watchdog(struct ifnet *);
+void mec_tick(void *);
+int mec_ioctl(struct ifnet *, u_long, caddr_t);
+void mec_reset(struct mec_softc *);
+void mec_setfilter(struct mec_softc *);
+int mec_intr(void *arg);
+void mec_stop(struct ifnet *, int);
+void mec_rxintr(struct mec_softc *, uint32_t);
+void mec_txintr(struct mec_softc *, uint32_t);
+void mec_shutdown(void *);
+
+int
+mec_match(struct device *parent, void *match, void *aux)
+{
+ struct confargs *ca = aux;
+
+ if (ca->ca_sys != SGI_O2 || strcmp(ca->ca_name, mec_cd.cd_name))
+ return (0);
+
+ return (1);
+}
+
+void
+mec_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct mec_softc *sc = (void *)self;
+ struct confargs *ca = aux;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ uint32_t command;
+ char *macaddr;
+ struct mii_softc *child;
+ bus_dma_segment_t seg;
+ int i, err, rseg;
+
+ sc->sc_st = ca->ca_iot;
+ if (bus_space_map(sc->sc_st, ca->ca_baseaddr, MEC_NREGS, 0,
+ &sc->sc_sh) != 0) {
+ printf(": can't map i/o space\n");
+ return;
+ }
+
+ /* set up DMA structures */
+ sc->sc_dmat = ca->ca_dmat;
+
+ /*
+ * Allocate the control data structures, and create and load the
+ * DMA map for it.
+ */
+ if ((err = bus_dmamem_alloc(sc->sc_dmat,
+ sizeof(struct mec_control_data), MEC_CONTROL_DATA_ALIGN, 0,
+ &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
+ printf(": unable to allocate control data, error = %d\n", err);
+ goto fail_0;
+ }
+
+ /*
+ * XXX needs re-think...
+ * control data structures contain whole RX data buffer, so
+ * BUS_DMA_COHERENT (which disables cache) may cause some performance
+ * issue on copying data from the RX buffer to mbuf on normal memory,
+ * though we have to make sure all bus_dmamap_sync(9) ops are called
+ * proprely in that case.
+ */
+ if ((err = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
+ sizeof(struct mec_control_data),
+ (caddr_t *)&sc->sc_control_data, /*BUS_DMA_COHERENT*/ 0)) != 0) {
+ printf(": unable to map control data, error = %d\n", err);
+ goto fail_1;
+ }
+ memset(sc->sc_control_data, 0, sizeof(struct mec_control_data));
+
+ if ((err = bus_dmamap_create(sc->sc_dmat,
+ sizeof(struct mec_control_data), 1,
+ sizeof(struct mec_control_data), 0, 0, &sc->sc_cddmamap)) != 0) {
+ printf(": unable to create control data DMA map, error = %d\n",
+ err);
+ goto fail_2;
+ }
+ if ((err = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap,
+ sc->sc_control_data, sizeof(struct mec_control_data), NULL,
+ BUS_DMA_NOWAIT)) != 0) {
+ printf(": unable to load control data DMA map, error = %d\n",
+ err);
+ goto fail_3;
+ }
+
+ /* create TX buffer DMA maps */
+ for (i = 0; i < MEC_NTXDESC; i++) {
+ if ((err = bus_dmamap_create(sc->sc_dmat,
+ MCLBYTES, 1, MCLBYTES, 0, 0,
+ &sc->sc_txsoft[i].txs_dmamap)) != 0) {
+ printf(": unable to create tx DMA map %d, error = %d\n",
+ i, err);
+ goto fail_4;
+ }
+ }
+
+ timeout_set(&sc->sc_tick_ch, mec_tick, sc);
+
+ /* get ehternet address from ARCBIOS */
+ if ((macaddr = Bios_GetEnvironmentVariable("eaddr")) == NULL) {
+ printf(": unable to get MAC address!\n");
+ goto fail_4;
+ }
+ enaddr_aton(macaddr, sc->sc_ac.ac_enaddr);
+
+ /* reset device */
+ mec_reset(sc);
+
+ command = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL);
+
+ printf(": MAC-110 rev %d, address %s\n",
+ (command & MEC_MAC_REVISION) >> MEC_MAC_REVISION_SHIFT,
+ ether_sprintf(sc->sc_ac.ac_enaddr));
+
+ /* Done, now attach everything */
+
+ sc->sc_mii.mii_ifp = ifp;
+ sc->sc_mii.mii_readreg = mec_mii_readreg;
+ sc->sc_mii.mii_writereg = mec_mii_writereg;
+ sc->sc_mii.mii_statchg = mec_statchg;
+
+ /* Set up PHY properties */
+ ifmedia_init(&sc->sc_mii.mii_media, 0, mec_mediachange,
+ mec_mediastatus);
+ mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
+ MII_OFFSET_ANY, 0);
+
+ child = LIST_FIRST(&sc->sc_mii.mii_phys);
+ if (child == NULL) {
+ /* No PHY attached */
+ ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL,
+ 0, NULL);
+ ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL);
+ } else {
+ ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
+ sc->sc_phyaddr = child->mii_phy;
+ }
+
+ bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = mec_ioctl;
+ ifp->if_start = mec_start;
+ ifp->if_watchdog = mec_watchdog;
+ ifp->if_mtu = ETHERMTU;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if_attach(ifp);
+ IFQ_SET_MAXLEN(&ifp->if_snd, MEC_NTXDESC - 1);
+ ether_ifattach(ifp);
+
+ /* establish interrupt */
+ BUS_INTR_ESTABLISH(ca, NULL, ca->ca_intr, IST_EDGE, IPL_NET,
+ mec_intr, sc, sc->sc_dev.dv_xname);
+
+ /* set shutdown hook to reset interface on powerdown */
+ sc->sc_sdhook = shutdownhook_establish(mec_shutdown, sc);
+
+ return;
+
+ /*
+ * Free any resources we've allocated during the failed attach
+ * attempt. Do this in reverse order and fall though.
+ */
+ fail_4:
+ for (i = 0; i < MEC_NTXDESC; i++) {
+ if (sc->sc_txsoft[i].txs_dmamap != NULL)
+ bus_dmamap_destroy(sc->sc_dmat,
+ sc->sc_txsoft[i].txs_dmamap);
+ }
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap);
+ fail_3:
+ bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap);
+ fail_2:
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_control_data,
+ sizeof(struct mec_control_data));
+ fail_1:
+ bus_dmamem_free(sc->sc_dmat, &seg, rseg);
+ fail_0:
+ return;
+}
+
+int
+mec_mii_readreg(struct device *self, int phy, int reg)
+{
+ struct mec_softc *sc = (void *)self;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ uint32_t val;
+ int i;
+
+ if (mec_mii_wait(sc) != 0)
+ return 0;
+
+ bus_space_write_4(st, sh, MEC_PHY_ADDRESS,
+ (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER));
+ bus_space_write_8(st, sh, MEC_PHY_READ_INITIATE, 1);
+ delay(25);
+
+ for (i = 0; i < 20; i++) {
+ delay(30);
+
+ val = bus_space_read_4(st, sh, MEC_PHY_DATA);
+
+ if ((val & MEC_PHY_DATA_BUSY) == 0)
+ return val & MEC_PHY_DATA_VALUE;
+ }
+ return 0;
+}
+
+void
+mec_mii_writereg(struct device *self, int phy, int reg, int val)
+{
+ struct mec_softc *sc = (void *)self;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+
+ if (mec_mii_wait(sc) != 0) {
+ printf("timed out writing %x: %x\n", reg, val);
+ return;
+ }
+
+ bus_space_write_4(st, sh, MEC_PHY_ADDRESS,
+ (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER));
+
+ delay(60);
+
+ bus_space_write_4(st, sh, MEC_PHY_DATA, val & MEC_PHY_DATA_VALUE);
+
+ delay(60);
+
+ mec_mii_wait(sc);
+}
+
+int
+mec_mii_wait(struct mec_softc *sc)
+{
+ uint32_t busy;
+ int i, s;
+
+ for (i = 0; i < 100; i++) {
+ delay(30);
+
+ s = splhigh();
+ busy = bus_space_read_4(sc->sc_st, sc->sc_sh, MEC_PHY_DATA);
+ splx(s);
+
+ if ((busy & MEC_PHY_DATA_BUSY) == 0)
+ return 0;
+ if (busy == 0xffff) /* XXX ? */
+ return 0;
+ }
+
+ printf("%s: MII timed out\n", sc->sc_dev.dv_xname);
+ return 1;
+}
+
+void
+mec_statchg(struct device *self)
+{
+ struct mec_softc *sc = (void *)self;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ uint32_t control;
+
+ control = bus_space_read_8(st, sh, MEC_MAC_CONTROL);
+ control &= ~(MEC_MAC_IPGT | MEC_MAC_IPGR1 | MEC_MAC_IPGR2 |
+ MEC_MAC_FULL_DUPLEX | MEC_MAC_SPEED_SELECT);
+
+ /* must also set IPG here for duplex stuff ... */
+ if ((sc->sc_mii.mii_media_active & IFM_FDX) != 0) {
+ control |= MEC_MAC_FULL_DUPLEX;
+ } else {
+ /* set IPG */
+ control |= MEC_MAC_IPG_DEFAULT;
+ }
+
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, control);
+}
+
+void
+mec_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct mec_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_flags & IFF_UP) == 0)
+ return;
+
+ mii_pollstat(&sc->sc_mii);
+ ifmr->ifm_status = sc->sc_mii.mii_media_status;
+ ifmr->ifm_active = sc->sc_mii.mii_media_active;
+}
+
+int
+mec_mediachange(struct ifnet *ifp)
+{
+ struct mec_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_flags & IFF_UP) == 0)
+ return 0;
+
+ return mii_mediachg(&sc->sc_mii);
+}
+
+/*
+ * XXX
+ * maybe this function should be moved to common part
+ * (sgimips/machdep.c or elsewhere) for all on-board network devices.
+ */
+static void
+enaddr_aton(const char *str, uint8_t *eaddr)
+{
+ int i;
+ char c;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (*str == ':')
+ str++;
+
+ c = *str++;
+ if ('0' <= c && c <= '9')
+ eaddr[i] = (c - '0');
+ else if ('A' <= c && c <= 'F')
+ eaddr[i] = c + 10 - 'A';
+ else if ('a' <= c && c <= 'f')
+ eaddr[i] = c + 10 - 'a';
+
+ c = *str++;
+ if ('0' <= c && c <= '9')
+ eaddr[i] = (eaddr[i] << 4) | (c - '0');
+ else if ('A' <= c && c <= 'F')
+ eaddr[i] = (eaddr[i] << 4) | (c + 10 - 'A');
+ else if ('a' <= c && c <= 'f')
+ eaddr[i] = (eaddr[i] << 4) | (c + 10 - 'a');
+ }
+}
+
+int
+mec_init(struct ifnet *ifp)
+{
+ struct mec_softc *sc = ifp->if_softc;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ struct mec_rxdesc *rxd;
+ int i;
+
+ /* cancel any pending I/O */
+ mec_stop(ifp, 0);
+
+ /* reset device */
+ mec_reset(sc);
+
+ /* setup filter for multicast or promisc mode */
+ mec_setfilter(sc);
+
+ /* set the TX ring pointer to the base address */
+ bus_space_write_8(st, sh, MEC_TX_RING_BASE, MEC_CDTXADDR(sc, 0));
+
+ sc->sc_txpending = 0;
+ sc->sc_txdirty = 0;
+ sc->sc_txlast = MEC_NTXDESC - 1;
+
+ /* put RX buffers into FIFO */
+ for (i = 0; i < MEC_NRXDESC; i++) {
+ rxd = &sc->sc_rxdesc[i];
+ rxd->rxd_stat = 0;
+ MEC_RXSTATSYNC(sc, i, BUS_DMASYNC_PREREAD);
+ MEC_RXBUFSYNC(sc, i, ETHER_MAX_LEN, BUS_DMASYNC_PREREAD);
+ bus_space_write_8(st, sh, MEC_MCL_RX_FIFO, MEC_CDRXADDR(sc, i));
+ }
+ sc->sc_rxptr = 0;
+
+#if 0 /* XXX no info */
+ bus_space_write_8(st, sh, MEC_TIMER, 0);
+#endif
+
+ /*
+ * MEC_DMA_TX_INT_ENABLE will be set later otherwise it causes
+ * spurious interrupts when TX buffers are empty
+ */
+ bus_space_write_8(st, sh, MEC_DMA_CONTROL,
+ (MEC_RXD_DMAOFFSET << MEC_DMA_RX_DMA_OFFSET_SHIFT) |
+ (MEC_NRXDESC << MEC_DMA_RX_INT_THRESH_SHIFT) |
+ MEC_DMA_TX_DMA_ENABLE | /* MEC_DMA_TX_INT_ENABLE | */
+ MEC_DMA_RX_DMA_ENABLE | MEC_DMA_RX_INT_ENABLE);
+
+ timeout_add(&sc->sc_tick_ch, hz);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ mec_start(ifp);
+
+ mii_mediachg(&sc->sc_mii);
+
+ return 0;
+}
+
+void
+mec_reset(struct mec_softc *sc)
+{
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ uint64_t address, control;
+ int i;
+
+ /* reset chip */
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, MEC_MAC_CORE_RESET);
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, 0);
+ delay(1000);
+
+ /* set ethernet address */
+ address = 0;
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ address = address << 8;
+ address += sc->sc_ac.ac_enaddr[i];
+ }
+ bus_space_write_8(st, sh, MEC_STATION, address);
+
+ /* Default to 100/half and let autonegotiation work its magic */
+ control = MEC_MAC_SPEED_SELECT | MEC_MAC_FILTER_MATCHMULTI |
+ MEC_MAC_IPG_DEFAULT;
+
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, control);
+ bus_space_write_8(st, sh, MEC_DMA_CONTROL, 0);
+
+ DPRINTF(MEC_DEBUG_RESET, ("mec: control now %llx\n",
+ bus_space_read_8(st, sh, MEC_MAC_CONTROL)));
+}
+
+void
+mec_start(struct ifnet *ifp)
+{
+ struct mec_softc *sc = ifp->if_softc;
+ struct mbuf *m0;
+ struct mec_txdesc *txd;
+ struct mec_txsoft *txs;
+ bus_dmamap_t dmamap;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ uint64_t txdaddr;
+ int error, firsttx, nexttx, opending;
+ int len, bufoff, buflen, unaligned, txdlen;
+
+ if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ /*
+ * Remember the previous txpending and the first transmit descriptor.
+ */
+ opending = sc->sc_txpending;
+ firsttx = MEC_NEXTTX(sc->sc_txlast);
+
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: opending = %d, firsttx = %d\n", opending, firsttx));
+
+ for (;;) {
+ /* Grab a packet off the queue. */
+ IFQ_POLL(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ if (sc->sc_txpending == MEC_NTXDESC) {
+ break;
+ }
+
+ /*
+ * Get the next available transmit descriptor.
+ */
+ nexttx = MEC_NEXTTX(sc->sc_txlast);
+ txd = &sc->sc_txdesc[nexttx];
+ txs = &sc->sc_txsoft[nexttx];
+
+ buflen = 0;
+ bufoff = 0;
+ txdaddr = 0; /* XXX gcc */
+ txdlen = 0; /* XXX gcc */
+
+ len = m0->m_pkthdr.len;
+
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: len = %d, nexttx = %d\n", len, nexttx));
+
+ IFQ_DEQUEUE(&ifp->if_snd, m0);
+ if (len < ETHER_PAD_LEN) {
+ /*
+ * I don't know if MEC chip does auto padding,
+ * so if the packet is small enough,
+ * just copy it to the buffer in txdesc.
+ * Maybe this is the simple way.
+ */
+ DPRINTF(MEC_DEBUG_START, ("mec_start: short packet\n"));
+
+ bufoff = MEC_TXD_BUFSTART(ETHER_PAD_LEN);
+ m_copydata(m0, 0, m0->m_pkthdr.len,
+ txd->txd_buf + bufoff);
+ memset(txd->txd_buf + bufoff + len, 0,
+ ETHER_PAD_LEN - len);
+ len = buflen = ETHER_PAD_LEN;
+
+ txs->txs_flags = MEC_TXS_TXDBUF | buflen;
+ } else {
+ /*
+ * If the packet won't fit the buffer in txdesc,
+ * we have to use concatinate pointer to handle it.
+ * While MEC can handle up to three segments to
+ * concatinate, MEC requires that both the second and
+ * third segments have to be 8 byte aligned.
+ * Since it's unlikely for mbuf clusters, we use
+ * only the first concatinate pointer. If the packet
+ * doesn't fit in one DMA segment, allocate new mbuf
+ * and copy the packet to it.
+ *
+ * Besides, if the start address of the first segments
+ * is not 8 byte aligned, such part have to be copied
+ * to the txdesc buffer. (XXX see below comments)
+ */
+ DPRINTF(MEC_DEBUG_START, ("mec_start: long packet\n"));
+
+ dmamap = txs->txs_dmamap;
+ if (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
+ BUS_DMA_WRITE | BUS_DMA_NOWAIT) != 0) {
+ struct mbuf *m;
+
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: re-allocating mbuf\n"));
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: unable to allocate "
+ "TX mbuf\n", sc->sc_dev.dv_xname);
+ break;
+ }
+ if (len > (MHLEN - ETHER_ALIGN)) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ printf("%s: unable to allocate "
+ "TX cluster\n",
+ sc->sc_dev.dv_xname);
+ m_freem(m);
+ break;
+ }
+ }
+ /*
+ * Each packet has the Ethernet header, so
+ * in many case the header isn't 4-byte aligned
+ * and data after the header is 4-byte aligned.
+ * Thus adding 2-byte offset before copying to
+ * new mbuf avoids unaligned copy and this may
+ * improve some performance.
+ * As noted above, unaligned part has to be
+ * copied to txdesc buffer so this may cause
+ * extra copy ops, but for now MEC always
+ * requires some data in txdesc buffer,
+ * so we always have to copy some data anyway.
+ */
+ m->m_data += ETHER_ALIGN;
+ m_copydata(m0, 0, len, mtod(m, caddr_t));
+ m->m_pkthdr.len = m->m_len = len;
+ m_freem(m0);
+ m0 = m;
+ error = bus_dmamap_load_mbuf(sc->sc_dmat,
+ dmamap, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: unable to load TX buffer, "
+ "error = %d\n",
+ sc->sc_dev.dv_xname, error);
+ m_freem(m);
+ break;
+ }
+ }
+
+ /* handle unaligned part */
+ txdaddr = MEC_TXD_ROUNDUP(dmamap->dm_segs[0].ds_addr);
+ txs->txs_flags = MEC_TXS_TXDPTR1;
+ unaligned =
+ dmamap->dm_segs[0].ds_addr & (MEC_TXD_ALIGN - 1);
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: ds_addr = 0x%x, unaligned = %d\n",
+ (u_int)dmamap->dm_segs[0].ds_addr, unaligned));
+ if (unaligned != 0) {
+ buflen = MEC_TXD_ALIGN - unaligned;
+ bufoff = MEC_TXD_BUFSTART(buflen);
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: unaligned, "
+ "buflen = %d, bufoff = %d\n",
+ buflen, bufoff));
+ memcpy(txd->txd_buf + bufoff,
+ mtod(m0, caddr_t), buflen);
+ txs->txs_flags |= MEC_TXS_TXDBUF | buflen;
+ }
+#if 1
+ else {
+ /*
+ * XXX needs hardware info XXX
+ * It seems MEC always requires some data
+ * in txd_buf[] even if buffer is
+ * 8-byte aligned otherwise DMA abort error
+ * occurs later...
+ */
+ buflen = MEC_TXD_ALIGN;
+ bufoff = MEC_TXD_BUFSTART(buflen);
+ memcpy(txd->txd_buf + bufoff,
+ mtod(m0, caddr_t), buflen);
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: aligned, "
+ "buflen = %d, bufoff = %d\n",
+ buflen, bufoff));
+ txs->txs_flags |= MEC_TXS_TXDBUF | buflen;
+ txdaddr += MEC_TXD_ALIGN;
+ }
+#endif
+ txdlen = len - buflen;
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: txdaddr = 0x%llx, txdlen = %d\n",
+ txdaddr, txdlen));
+
+ /*
+ * sync the DMA map for TX mbuf
+ *
+ * XXX unaligned part doesn't have to be sync'ed,
+ * but it's harmless...
+ */
+ bus_dmamap_sync(sc->sc_dmat, dmamap, 0,
+ dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+ }
+
+#if NBPFILTER > 0
+ /*
+ * Pass packet to bpf if there is a listener.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+
+ /*
+ * setup the transmit descriptor.
+ */
+
+ /* TXINT bit will be set later on the last packet */
+ txd->txd_cmd = (len - 1);
+ /* but also set TXINT bit on a half of TXDESC */
+ if (sc->sc_txpending == (MEC_NTXDESC / 2))
+ txd->txd_cmd |= MEC_TXCMD_TXINT;
+
+ if (txs->txs_flags & MEC_TXS_TXDBUF)
+ txd->txd_cmd |= TXCMD_BUFSTART(MEC_TXDESCSIZE - buflen);
+ if (txs->txs_flags & MEC_TXS_TXDPTR1) {
+ txd->txd_cmd |= MEC_TXCMD_PTR1;
+ txd->txd_ptr[0] = TXPTR_LEN(txdlen - 1) | txdaddr;
+ /*
+ * Store a pointer to the packet so we can
+ * free it later.
+ */
+ txs->txs_mbuf = m0;
+ } else {
+ txd->txd_ptr[0] = 0;
+ /*
+ * In this case all data are copied to buffer in txdesc,
+ * we can free TX mbuf here.
+ */
+ m_freem(m0);
+ }
+
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: txd_cmd = 0x%llx, txd_ptr = 0x%llx\n",
+ txd->txd_cmd, txd->txd_ptr[0]));
+ DPRINTF(MEC_DEBUG_START,
+ ("mec_start: len = %d (0x%04x), buflen = %d (0x%02x)\n",
+ len, len, buflen, buflen));
+
+ /* sync TX descriptor */
+ MEC_TXDESCSYNC(sc, nexttx,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ /* advance the TX pointer. */
+ sc->sc_txpending++;
+ sc->sc_txlast = nexttx;
+ }
+
+ if (sc->sc_txpending == MEC_NTXDESC) {
+ /* No more slots; notify upper layer. */
+ ifp->if_flags |= IFF_OACTIVE;
+ }
+
+ if (sc->sc_txpending != opending) {
+ /*
+ * Cause a TX interrupt to happen on the last packet
+ * we enqueued.
+ */
+ sc->sc_txdesc[sc->sc_txlast].txd_cmd |= MEC_TXCMD_TXINT;
+ MEC_TXCMDSYNC(sc, sc->sc_txlast,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ /* start TX */
+ bus_space_write_8(st, sh, MEC_TX_RING_PTR,
+ MEC_NEXTTX(sc->sc_txlast));
+
+ /*
+ * If the transmitter was idle,
+ * reset the txdirty pointer and reenable TX interrupt.
+ */
+ if (opending == 0) {
+ sc->sc_txdirty = firsttx;
+ bus_space_write_8(st, sh, MEC_TX_ALIAS,
+ MEC_TX_ALIAS_INT_ENABLE);
+ }
+
+ /* Set a watchdog timer in case the chip flakes out. */
+ ifp->if_timer = 5;
+ }
+}
+
+void
+mec_stop(struct ifnet *ifp, int disable)
+{
+ struct mec_softc *sc = ifp->if_softc;
+ struct mec_txsoft *txs;
+ int i;
+
+ DPRINTF(MEC_DEBUG_STOP, ("mec_stop\n"));
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+ timeout_del(&sc->sc_tick_ch);
+ mii_down(&sc->sc_mii);
+
+ /* release any TX buffers */
+ for (i = 0; i < MEC_NTXDESC; i++) {
+ txs = &sc->sc_txsoft[i];
+ if ((txs->txs_flags & MEC_TXS_TXDPTR1) != 0) {
+ bus_dmamap_unload(sc->sc_dmat, txs->txs_dmamap);
+ m_freem(txs->txs_mbuf);
+ txs->txs_mbuf = NULL;
+ }
+ }
+}
+
+int
+mec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct mec_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ int s, error;
+
+ s = splimp();
+
+ if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) {
+ splx(s);
+ return (error);
+ }
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ mec_init(ifp);
+ arp_ifinit(&sc->sc_ac, ifa);
+ break;
+#endif
+ default:
+ mec_init(ifp);
+ break;
+ }
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN)
+ error = EINVAL;
+ else if (ifp->if_mtu != ifr->ifr_mtu)
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked up and not running, then start it.
+ * If it is marked down and running, stop it.
+ * XXX If it's up then re-initialize it. This is so flags
+ * such as IFF_PROMISC are handled.
+ */
+ if (ifp->if_flags & IFF_UP)
+ mec_init(ifp);
+ else if (ifp->if_flags & IFF_RUNNING)
+ mec_stop(ifp, 1);
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (cmd == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_ac) :
+ ether_delmulti(ifr, &sc->sc_ac);
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware
+ * filter accordingly.
+ */
+ mec_init(ifp);
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
+ break;
+
+ default:
+ error = ENXIO;
+ break;
+ }
+
+ splx(s);
+ return error;
+}
+
+void
+mec_watchdog(struct ifnet *ifp)
+{
+ struct mec_softc *sc = ifp->if_softc;
+
+ printf("%s: device timeout\n", sc->sc_dev.dv_xname);
+ ifp->if_oerrors++;
+
+ mec_init(ifp);
+}
+
+void
+mec_tick(void *arg)
+{
+ struct mec_softc *sc = arg;
+ int s;
+
+ s = splnet();
+ mii_tick(&sc->sc_mii);
+ splx(s);
+
+ timeout_add(&sc->sc_tick_ch, hz);
+}
+
+void
+mec_setfilter(struct mec_softc *sc)
+{
+ struct arpcom *ec = &sc->sc_ac;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ uint64_t mchash;
+ uint32_t control, hash;
+ int mcnt;
+
+ control = bus_space_read_8(st, sh, MEC_MAC_CONTROL);
+ control &= ~MEC_MAC_FILTER_MASK;
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ control |= MEC_MAC_FILTER_PROMISC;
+ bus_space_write_8(st, sh, MEC_MULTICAST, 0xffffffffffffffffULL);
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, control);
+ return;
+ }
+
+ mcnt = 0;
+ mchash = 0;
+ ETHER_FIRST_MULTI(step, ec, enm);
+ while (enm != NULL) {
+ if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
+ /* set allmulti for a range of multicast addresses */
+ control |= MEC_MAC_FILTER_ALLMULTI;
+ bus_space_write_8(st, sh, MEC_MULTICAST,
+ 0xffffffffffffffffULL);
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, control);
+ return;
+ }
+
+#define mec_calchash(addr) (ether_crc32_be((addr), ETHER_ADDR_LEN) >> 26)
+
+ hash = mec_calchash(enm->enm_addrlo);
+ mchash |= 1 << hash;
+ mcnt++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ ifp->if_flags &= ~IFF_ALLMULTI;
+
+ if (mcnt > 0)
+ control |= MEC_MAC_FILTER_MATCHMULTI;
+
+ bus_space_write_8(st, sh, MEC_MULTICAST, mchash);
+ bus_space_write_8(st, sh, MEC_MAC_CONTROL, control);
+}
+
+int
+mec_intr(void *arg)
+{
+ struct mec_softc *sc = arg;
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ uint32_t statreg, statack, dmac;
+ int handled, sent;
+
+ DPRINTF(MEC_DEBUG_INTR, ("mec_intr: called\n"));
+
+ handled = sent = 0;
+
+ for (;;) {
+ statreg = bus_space_read_8(st, sh, MEC_INT_STATUS);
+
+ DPRINTF(MEC_DEBUG_INTR,
+ ("mec_intr: INT_STAT = 0x%x\n", statreg));
+
+ statack = statreg & MEC_INT_STATUS_MASK;
+ if (statack == 0)
+ break;
+ bus_space_write_8(st, sh, MEC_INT_STATUS, statack);
+
+ handled = 1;
+
+ if (statack &
+ (MEC_INT_RX_THRESHOLD |
+ MEC_INT_RX_FIFO_UNDERFLOW)) {
+ mec_rxintr(sc, statreg);
+ }
+
+ dmac = bus_space_read_8(st, sh, MEC_DMA_CONTROL);
+ DPRINTF(MEC_DEBUG_INTR,
+ ("mec_intr: DMA_CONT = 0x%x\n", dmac));
+
+ if (statack &
+ (MEC_INT_TX_EMPTY |
+ MEC_INT_TX_PACKET_SENT |
+ MEC_INT_TX_ABORT)) {
+ mec_txintr(sc, statreg);
+ sent = 1;
+ }
+
+ if (statack &
+ (MEC_INT_TX_LINK_FAIL |
+ MEC_INT_TX_MEM_ERROR |
+ MEC_INT_TX_ABORT |
+ MEC_INT_RX_DMA_UNDERFLOW)) {
+ printf("%s: mec_intr: interrupt status = 0x%x\n",
+ sc->sc_dev.dv_xname, statreg);
+ }
+ }
+
+ if (sent) {
+ /* try to get more packets going */
+ mec_start(ifp);
+ }
+
+ return handled;
+}
+
+void
+mec_rxintr(struct mec_softc *sc, uint32_t stat)
+{
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct mbuf *m;
+ struct mec_rxdesc *rxd;
+ uint64_t rxstat;
+ u_int len;
+ int i, last;
+
+ DPRINTF(MEC_DEBUG_RXINTR, ("mec_rxintr: called\n"));
+
+ bus_space_write_8(st, sh, MEC_RX_ALIAS, 0);
+
+ last = (stat & MEC_INT_RX_MCL_FIFO_ALIAS) >> 8;
+ if (stat & MEC_INT_RX_FIFO_UNDERFLOW)
+ last = (last - 1) & MEC_NRXDESC_MASK;
+
+ DPRINTF(MEC_DEBUG_RXINTR, ("mec_rxintr: rxptr %d last %d\n",
+ sc->sc_rxptr, last));
+ for (i = sc->sc_rxptr; i != last; i = MEC_NEXTRX(i)) {
+
+ MEC_RXSTATSYNC(sc, i, BUS_DMASYNC_POSTREAD);
+ rxd = &sc->sc_rxdesc[i];
+ rxstat = rxd->rxd_stat;
+
+ DPRINTF(MEC_DEBUG_RXINTR,
+ ("mec_rxintr: rxstat = 0x%llx, rxptr = %d\n",
+ rxstat, i));
+ DPRINTF(MEC_DEBUG_RXINTR, ("mec_rxintr: rxfifo = 0x%x\n",
+ (u_int)bus_space_read_8(st, sh, MEC_RX_FIFO)));
+
+ if ((rxstat & MEC_RXSTAT_RECEIVED) == 0) {
+ MEC_RXSTATSYNC(sc, i, BUS_DMASYNC_PREREAD);
+ break;
+ }
+
+ len = rxstat & MEC_RXSTAT_LEN;
+
+ if (len < ETHER_MIN_LEN ||
+ len > ETHER_MAX_LEN) {
+ /* invalid length packet; drop it. */
+ DPRINTF(MEC_DEBUG_RXINTR,
+ ("mec_rxintr: wrong packet\n"));
+ dropit:
+ ifp->if_ierrors++;
+ rxd->rxd_stat = 0;
+ MEC_RXSTATSYNC(sc, i, BUS_DMASYNC_PREREAD);
+ bus_space_write_8(st, sh, MEC_MCL_RX_FIFO,
+ MEC_CDRXADDR(sc, i));
+ continue;
+ }
+
+ if (rxstat &
+ (MEC_RXSTAT_BADPACKET |
+ MEC_RXSTAT_LONGEVENT |
+ MEC_RXSTAT_INVALID |
+ MEC_RXSTAT_CRCERROR |
+ MEC_RXSTAT_VIOLATION)) {
+ printf("%s: mec_rxintr: status = 0x%llx\n",
+ sc->sc_dev.dv_xname, rxstat);
+ goto dropit;
+ }
+
+ /*
+ * now allocate an mbuf (and possibly a cluster) to hold
+ * the received packet.
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: unable to allocate RX mbuf\n",
+ sc->sc_dev.dv_xname);
+ goto dropit;
+ }
+ if (len > (MHLEN - ETHER_ALIGN)) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ printf("%s: unable to allocate RX cluster\n",
+ sc->sc_dev.dv_xname);
+ m_freem(m);
+ m = NULL;
+ goto dropit;
+ }
+ }
+
+ /*
+ * Note MEC chip seems to insert 2 byte paddingat the start of
+ * RX buffer, but we copy whole buffer to avoid unaligned copy.
+ */
+ MEC_RXBUFSYNC(sc, i, len + ETHER_ALIGN, BUS_DMASYNC_POSTREAD);
+ memcpy(mtod(m, caddr_t), rxd->rxd_buf,
+ ETHER_ALIGN + len - ETHER_CRC_LEN);
+ MEC_RXBUFSYNC(sc, i, ETHER_MAX_LEN, BUS_DMASYNC_PREREAD);
+ m->m_data += ETHER_ALIGN;
+
+ /* put RX buffer into FIFO again */
+ rxd->rxd_stat = 0;
+ MEC_RXSTATSYNC(sc, i, BUS_DMASYNC_PREREAD);
+ bus_space_write_8(st, sh, MEC_MCL_RX_FIFO, MEC_CDRXADDR(sc, i));
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len - ETHER_CRC_LEN;
+
+ ifp->if_ipackets++;
+
+#if NBPFILTER > 0
+ /*
+ * Pass this up to any BPF listeners, but only
+ * pass it up the stack it its for us.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ /* Pass it on. */
+ ether_input_mbuf(ifp, m);
+ }
+
+ /* update RX pointer */
+ sc->sc_rxptr = i;
+
+ bus_space_write_8(st, sh, MEC_RX_ALIAS,
+ (MEC_NRXDESC << MEC_DMA_RX_INT_THRESH_SHIFT) |
+ MEC_DMA_RX_INT_ENABLE);
+}
+
+void
+mec_txintr(struct mec_softc *sc, uint32_t stat)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct mec_txdesc *txd;
+ struct mec_txsoft *txs;
+ bus_dmamap_t dmamap;
+ uint64_t txstat;
+ int i, last;
+ u_int col;
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ DPRINTF(MEC_DEBUG_TXINTR, ("mec_txintr: called\n"));
+
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_TX_ALIAS, 0);
+ last = (stat & MEC_INT_TX_RING_BUFFER_ALIAS) >> 16;
+
+ DPRINTF(MEC_DEBUG_TXINTR, ("mec_txintr: dirty %d last %d\n",
+ sc->sc_txdirty, last));
+ for (i = sc->sc_txdirty; i != last && sc->sc_txpending != 0;
+ i = MEC_NEXTTX(i), sc->sc_txpending--) {
+ txd = &sc->sc_txdesc[i];
+
+ MEC_TXDESCSYNC(sc, i,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+
+ txstat = txd->txd_stat;
+ DPRINTF(MEC_DEBUG_TXINTR,
+ ("mec_txintr: dirty = %d, txstat = 0x%llx\n",
+ i, txstat));
+ if ((txstat & MEC_TXSTAT_SENT) == 0) {
+ MEC_TXCMDSYNC(sc, i, BUS_DMASYNC_PREREAD);
+ break;
+ }
+
+ if ((txstat & MEC_TXSTAT_SUCCESS) == 0) {
+ printf("%s: TX error: txstat = 0x%llx\n",
+ sc->sc_dev.dv_xname, txstat);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ txs = &sc->sc_txsoft[i];
+ if ((txs->txs_flags & MEC_TXS_TXDPTR1) != 0) {
+ dmamap = txs->txs_dmamap;
+ bus_dmamap_sync(sc->sc_dmat, dmamap, 0,
+ dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, dmamap);
+ m_freem(txs->txs_mbuf);
+ txs->txs_mbuf = NULL;
+ }
+
+ col = (txstat & MEC_TXSTAT_COLCNT) >> MEC_TXSTAT_COLCNT_SHIFT;
+ ifp->if_collisions += col;
+ ifp->if_opackets++;
+ }
+
+ /* update the dirty TX buffer pointer */
+ sc->sc_txdirty = i;
+ DPRINTF(MEC_DEBUG_INTR,
+ ("mec_txintr: sc_txdirty = %2d, sc_txpending = %2d\n",
+ sc->sc_txdirty, sc->sc_txpending));
+
+ /* cancel the watchdog timer if there are no pending TX packets */
+ if (sc->sc_txpending == 0)
+ ifp->if_timer = 0;
+ else if (!(stat & MEC_INT_TX_EMPTY))
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_TX_ALIAS,
+ MEC_TX_ALIAS_INT_ENABLE);
+}
+
+void
+mec_shutdown(void *arg)
+{
+ struct mec_softc *sc = arg;
+
+ mec_stop(&sc->sc_ac.ac_if, 1);
+}
diff --git a/sys/arch/sgi/dev/if_mecreg.h b/sys/arch/sgi/dev/if_mecreg.h
new file mode 100644
index 00000000000..a8362b7c65b
--- /dev/null
+++ b/sys/arch/sgi/dev/if_mecreg.h
@@ -0,0 +1,151 @@
+/* $OpenBSD: if_mecreg.h,v 1.1 2004/08/10 07:30:57 mickey Exp $ */
+/* $NetBSD: if_mecreg.h,v 1.2 2004/07/11 03:13:04 tsutsui Exp $ */
+
+/*
+ * Copyright (c) 2001 Christopher Sekiya
+ * Copyright (c) 2000 Soren S. Jorvang
+ * 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 for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*
+ * MACE MAC110 ethernet register definitions
+ */
+
+#define MEC_MAC_CONTROL 0x00
+#define MEC_MAC_CORE_RESET 0x0000000000000001 /* reset signal */
+#define MEC_MAC_FULL_DUPLEX 0x0000000000000002 /* 1 to enable */
+#define MEC_MAC_INT_LOOPBACK 0x0000000000000004 /* 0 = normal op */
+#define MEC_MAC_SPEED_SELECT 0x0000000000000008 /* 0/1 10/100 */
+#define MEC_MAC_MII_SELECT 0x0000000000000010 /* MII/SIA */
+#define MEC_MAC_FILTER_MASK 0x0000000000000060
+#define MEC_MAC_FILTER_STATION 0x0000000000000000
+#define MEC_MAC_FILTER_MATCHMULTI 0x0000000000000020
+#define MEC_MAC_FILTER_ALLMULTI 0x0000000000000040
+#define MEC_MAC_FILTER_PROMISC 0x0000000000000060
+#define MEC_MAC_LINK_FAILURE 0x0000000000000080
+#define MEC_MAC_IPGT 0x0000000000007f00 /* interpacket gap */
+#define MEC_MAC_IPGT_SHIFT 8
+#define MEC_MAC_IPGR1 0x00000000003f8000
+#define MEC_MAC_IPGR1_SHIFT 15
+#define MEC_MAC_IPGR2 0x000000001fc00000
+#define MEC_MAC_IPGR2_SHIFT 22
+#define MEC_MAC_REVISION 0x00000000e0000000
+#define MEC_MAC_REVISION_SHIFT 29
+
+#define MEC_MAC_IPG_DEFAULT \
+ (21 << MEC_MAC_IPGT_SHIFT) | \
+ (17 << MEC_MAC_IPGR1_SHIFT) | \
+ (11 << MEC_MAC_IPGR2_SHIFT)
+
+#define MEC_INT_STATUS 0x08
+#define MEC_INT_STATUS_MASK 0x00000000000000ff
+#define MEC_INT_TX_EMPTY 0x0000000000000001
+#define MEC_INT_TX_PACKET_SENT 0x0000000000000002
+#define MEC_INT_TX_LINK_FAIL 0x0000000000000004
+#define MEC_INT_TX_MEM_ERROR 0x0000000000000008
+#define MEC_INT_TX_ABORT 0x0000000000000010
+#define MEC_INT_RX_THRESHOLD 0x0000000000000020
+#define MEC_INT_RX_FIFO_UNDERFLOW 0x0000000000000040
+#define MEC_INT_RX_DMA_UNDERFLOW 0x0000000000000080
+#define MEC_INT_RX_MCL_FIFO_ALIAS 0x0000000000001f00
+#define MEC_INT_TX_RING_BUFFER_ALIAS 0x0000000001ff0000
+#define MEC_INT_RX_SEQUENCE_NUMBER 0x000000003e000000
+#define MEC_INT_MCAST_HASH_OUTPUT 0x0000000040000000
+
+#define MEC_DMA_CONTROL 0x10
+#define MEC_DMA_TX_INT_ENABLE 0x0000000000000001
+#define MEC_DMA_TX_DMA_ENABLE 0x0000000000000002
+#define MEC_DMA_TX_RING_SIZE_MASK 0x000000000000000c
+#define MEC_DMA_RX_INT_THRESHOLD 0x00000000000001f0
+#define MEC_DMA_RX_INT_THRESH_SHIFT 4
+#define MEC_DMA_RX_INT_ENABLE 0x0000000000000200
+#define MEC_DMA_RX_RUNT 0x0000000000000400
+#define MEC_DMA_RX_PACKET_GATHER 0x0000000000000800
+#define MEC_DMA_RX_DMA_OFFSET 0x0000000000007000
+#define MEC_DMA_RX_DMA_OFFSET_SHIFT 12
+#define MEC_DMA_RX_DMA_ENABLE 0x0000000000008000
+
+#define MEC_TIMER 0x18
+#define MEC_TX_ALIAS 0x20
+#define MEC_TX_ALIAS_INT_ENABLE 0x0000000000000001
+
+#define MEC_RX_ALIAS 0x28
+#define MEC_RX_ALIAS_INT_ENABLE 0x0000000000000200
+#define MEC_RX_ALIAS_INT_THRESHOLD 0x00000000000001f0
+
+#define MEC_TX_RING_PTR 0x30
+#define MEC_TX_RING_WRITE_PTR 0x00000000000001ff
+#define MEC_TX_RING_READ_PTR 0x0000000001ff0000
+#define MEC_TX_RING_PTR_ALIAS 0x38
+
+#define MEC_RX_FIFO 0x40
+#define MEC_RX_FIFO_ELEMENT_COUNT 0x000000000000001f
+#define MEC_RX_FIFO_READ_PTR 0x0000000000000f00
+#define MEC_RX_FIFO_GEN_NUMBER 0x0000000000001000
+#define MEC_RX_FIFO_WRITE_PTR 0x00000000000f0000
+#define MEC_RX_FIFO_GEN_NUMBER_2 0x0000000000100000
+
+#define MEC_RX_FIFO_ALIAS1 0x48
+#define MEC_RX_FIFO_ALIAS2 0x50
+#define MEC_TX_VECTOR 0x58
+#define MEC_IRQ_VECTOR 0x58
+
+#define MEC_PHY_DATA_PAD 0x60 /* XXX ? */
+#define MEC_PHY_DATA 0x64
+#define MEC_PHY_DATA_BUSY 0x00010000
+#define MEC_PHY_DATA_VALUE 0x0000ffff
+
+#define MEC_PHY_ADDRESS_PAD 0x68 /* XXX ? */
+#define MEC_PHY_ADDRESS 0x6c
+#define MEC_PHY_ADDR_REGISTER 0x0000001f
+#define MEC_PHY_ADDR_DEVICE 0x000003e0
+#define MEC_PHY_ADDR_DEVSHIFT 5
+
+#define MEC_PHY_READ_INITIATE 0x70
+#define MEC_PHY_BACKOFF 0x78
+
+#define MEC_STATION 0xa0
+#define MEC_STATION_ALT 0xa8
+#define MEC_STATION_MASK 0x0000ffffffffffffULL
+
+#define MEC_MULTICAST 0xb0
+#define MEC_TX_RING_BASE 0xb8
+#define MEC_TX_PKT1_CMD_1 0xc0
+#define MEC_TX_PKT1_BUFFER_1 0xc8
+#define MEC_TX_PKT1_BUFFER_2 0xd0
+#define MEC_TX_PKT1_BUFFER_3 0xd8
+#define MEC_TX_PKT2_CMD_1 0xe0
+#define MEC_TX_PKT2_BUFFER_1 0xe8
+#define MEC_TX_PKT2_BUFFER_2 0xf0
+#define MEC_TX_PKT2_BUFFER_3 0xf8
+
+#define MEC_MCL_RX_FIFO 0x100
+
+#define MEC_NREGS 0x200