[Midnightbsd-cvs] src [10071] trunk/sys/dev/rl: re-add rl

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun May 27 19:14:32 EDT 2018


Revision: 10071
          http://svnweb.midnightbsd.org/src/?rev=10071
Author:   laffer1
Date:     2018-05-27 19:14:32 -0400 (Sun, 27 May 2018)
Log Message:
-----------
re-add rl

Added Paths:
-----------
    trunk/sys/dev/rl/
    trunk/sys/dev/rl/if_rl.c
    trunk/sys/dev/rl/if_rlreg.h

Added: trunk/sys/dev/rl/if_rl.c
===================================================================
--- trunk/sys/dev/rl/if_rl.c	                        (rev 0)
+++ trunk/sys/dev/rl/if_rl.c	2018-05-27 23:14:32 UTC (rev 10071)
@@ -0,0 +1,2125 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 1997, 1998
+ *	Bill Paul <wpaul at ctr.columbia.edu>.  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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/rl/if_rl.c 312363 2017-01-18 02:22:07Z yongari $");
+
+/*
+ * RealTek 8129/8139 PCI NIC driver
+ *
+ * Supports several extremely cheap PCI 10/100 adapters based on
+ * the RealTek chipset. Datasheets can be obtained from
+ * www.realtek.com.tw.
+ *
+ * Written by Bill Paul <wpaul at ctr.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+/*
+ * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' This is
+ * probably the worst PCI ethernet controller ever made, with the possible
+ * exception of the FEAST chip made by SMC. The 8139 supports bus-master
+ * DMA, but it has a terrible interface that nullifies any performance
+ * gains that bus-master DMA usually offers.
+ *
+ * For transmission, the chip offers a series of four TX descriptor
+ * registers. Each transmit frame must be in a contiguous buffer, aligned
+ * on a longword (32-bit) boundary. This means we almost always have to
+ * do mbuf copies in order to transmit a frame, except in the unlikely
+ * case where a) the packet fits into a single mbuf, and b) the packet
+ * is 32-bit aligned within the mbuf's data area. The presence of only
+ * four descriptor registers means that we can never have more than four
+ * packets queued for transmission at any one time.
+ *
+ * Reception is not much better. The driver has to allocate a single large
+ * buffer area (up to 64K in size) into which the chip will DMA received
+ * frames. Because we don't know where within this region received packets
+ * will begin or end, we have no choice but to copy data from the buffer
+ * area into mbufs in order to pass the packets up to the higher protocol
+ * levels.
+ *
+ * It's impossible given this rotten design to really achieve decent
+ * performance at 100Mbps, unless you happen to have a 400Mhz PII or
+ * some equally overmuscled CPU to drive it.
+ *
+ * On the bright side, the 8139 does have a built-in PHY, although
+ * rather than using an MDIO serial interface like most other NICs, the
+ * PHY registers are directly accessible through the 8139's register
+ * space. The 8139 supports autonegotiation, as well as a 64-bit multicast
+ * filter.
+ *
+ * The 8129 chip is an older version of the 8139 that uses an external PHY
+ * chip. The 8129 has a serial MDIO interface for accessing the MII where
+ * the 8139 lets you directly access the on-board PHY registers. We need
+ * to select which interface to use depending on the chip type.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_device_polling.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/mii_bitbang.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+MODULE_DEPEND(rl, pci, 1, 1, 1);
+MODULE_DEPEND(rl, ether, 1, 1, 1);
+MODULE_DEPEND(rl, miibus, 1, 1, 1);
+
+/* "device miibus" required.  See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#include <dev/rl/if_rlreg.h>
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static const struct rl_type rl_devs[] = {
+	{ RT_VENDORID, RT_DEVICEID_8129, RL_8129,
+		"RealTek 8129 10/100BaseTX" },
+	{ RT_VENDORID, RT_DEVICEID_8139, RL_8139,
+		"RealTek 8139 10/100BaseTX" },
+	{ RT_VENDORID, RT_DEVICEID_8139D, RL_8139,
+		"RealTek 8139 10/100BaseTX" },
+	{ RT_VENDORID, RT_DEVICEID_8138, RL_8139,
+		"RealTek 8139 10/100BaseTX CardBus" },
+	{ RT_VENDORID, RT_DEVICEID_8100, RL_8139,
+		"RealTek 8100 10/100BaseTX" },
+	{ ACCTON_VENDORID, ACCTON_DEVICEID_5030, RL_8139,
+		"Accton MPX 5030/5038 10/100BaseTX" },
+	{ DELTA_VENDORID, DELTA_DEVICEID_8139, RL_8139,
+		"Delta Electronics 8139 10/100BaseTX" },
+	{ ADDTRON_VENDORID, ADDTRON_DEVICEID_8139, RL_8139,
+		"Addtron Technology 8139 10/100BaseTX" },
+	{ DLINK_VENDORID, DLINK_DEVICEID_520TX_REVC1, RL_8139,
+		"D-Link DFE-520TX (rev. C1) 10/100BaseTX" },
+	{ DLINK_VENDORID, DLINK_DEVICEID_530TXPLUS, RL_8139,
+		"D-Link DFE-530TX+ 10/100BaseTX" },
+	{ DLINK_VENDORID, DLINK_DEVICEID_690TXD, RL_8139,
+		"D-Link DFE-690TXD 10/100BaseTX" },
+	{ NORTEL_VENDORID, ACCTON_DEVICEID_5030, RL_8139,
+		"Nortel Networks 10/100BaseTX" },
+	{ COREGA_VENDORID, COREGA_DEVICEID_FETHERCBTXD, RL_8139,
+		"Corega FEther CB-TXD" },
+	{ COREGA_VENDORID, COREGA_DEVICEID_FETHERIICBTXD, RL_8139,
+		"Corega FEtherII CB-TXD" },
+	{ PEPPERCON_VENDORID, PEPPERCON_DEVICEID_ROLF, RL_8139,
+		"Peppercon AG ROL-F" },
+	{ PLANEX_VENDORID, PLANEX_DEVICEID_FNW3603TX, RL_8139,
+		"Planex FNW-3603-TX" },
+	{ PLANEX_VENDORID, PLANEX_DEVICEID_FNW3800TX, RL_8139,
+		"Planex FNW-3800-TX" },
+	{ CP_VENDORID, RT_DEVICEID_8139, RL_8139,
+		"Compaq HNE-300" },
+	{ LEVEL1_VENDORID, LEVEL1_DEVICEID_FPC0106TX, RL_8139,
+		"LevelOne FPC-0106TX" },
+	{ EDIMAX_VENDORID, EDIMAX_DEVICEID_EP4103DL, RL_8139,
+		"Edimax EP-4103DL CardBus" }
+};
+
+static int rl_attach(device_t);
+static int rl_detach(device_t);
+static void rl_dmamap_cb(void *, bus_dma_segment_t *, int, int);
+static int rl_dma_alloc(struct rl_softc *);
+static void rl_dma_free(struct rl_softc *);
+static void rl_eeprom_putbyte(struct rl_softc *, int);
+static void rl_eeprom_getword(struct rl_softc *, int, uint16_t *);
+static int rl_encap(struct rl_softc *, struct mbuf **);
+static int rl_list_tx_init(struct rl_softc *);
+static int rl_list_rx_init(struct rl_softc *);
+static int rl_ifmedia_upd(struct ifnet *);
+static void rl_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int rl_ioctl(struct ifnet *, u_long, caddr_t);
+static void rl_intr(void *);
+static void rl_init(void *);
+static void rl_init_locked(struct rl_softc *sc);
+static int rl_miibus_readreg(device_t, int, int);
+static void rl_miibus_statchg(device_t);
+static int rl_miibus_writereg(device_t, int, int, int);
+#ifdef DEVICE_POLLING
+static int rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count);
+static int rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count);
+#endif
+static int rl_probe(device_t);
+static void rl_read_eeprom(struct rl_softc *, uint8_t *, int, int, int);
+static void rl_reset(struct rl_softc *);
+static int rl_resume(device_t);
+static int rl_rxeof(struct rl_softc *);
+static void rl_rxfilter(struct rl_softc *);
+static int rl_shutdown(device_t);
+static void rl_start(struct ifnet *);
+static void rl_start_locked(struct ifnet *);
+static void rl_stop(struct rl_softc *);
+static int rl_suspend(device_t);
+static void rl_tick(void *);
+static void rl_txeof(struct rl_softc *);
+static void rl_watchdog(struct rl_softc *);
+static void rl_setwol(struct rl_softc *);
+static void rl_clrwol(struct rl_softc *);
+
+/*
+ * MII bit-bang glue
+ */
+static uint32_t rl_mii_bitbang_read(device_t);
+static void rl_mii_bitbang_write(device_t, uint32_t);
+
+static const struct mii_bitbang_ops rl_mii_bitbang_ops = {
+	rl_mii_bitbang_read,
+	rl_mii_bitbang_write,
+	{
+		RL_MII_DATAOUT,	/* MII_BIT_MDO */
+		RL_MII_DATAIN,	/* MII_BIT_MDI */
+		RL_MII_CLK,	/* MII_BIT_MDC */
+		RL_MII_DIR,	/* MII_BIT_DIR_HOST_PHY */
+		0,		/* MII_BIT_DIR_PHY_HOST */
+	}
+};
+
+static device_method_t rl_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		rl_probe),
+	DEVMETHOD(device_attach,	rl_attach),
+	DEVMETHOD(device_detach,	rl_detach),
+	DEVMETHOD(device_suspend,	rl_suspend),
+	DEVMETHOD(device_resume,	rl_resume),
+	DEVMETHOD(device_shutdown,	rl_shutdown),
+
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	rl_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	rl_miibus_writereg),
+	DEVMETHOD(miibus_statchg,	rl_miibus_statchg),
+
+	DEVMETHOD_END
+};
+
+static driver_t rl_driver = {
+	"rl",
+	rl_methods,
+	sizeof(struct rl_softc)
+};
+
+static devclass_t rl_devclass;
+
+DRIVER_MODULE(rl, pci, rl_driver, rl_devclass, 0, 0);
+DRIVER_MODULE(rl, cardbus, rl_driver, rl_devclass, 0, 0);
+DRIVER_MODULE(miibus, rl, miibus_driver, miibus_devclass, 0, 0);
+
+#define EE_SET(x)					\
+	CSR_WRITE_1(sc, RL_EECMD,			\
+		CSR_READ_1(sc, RL_EECMD) | x)
+
+#define EE_CLR(x)					\
+	CSR_WRITE_1(sc, RL_EECMD,			\
+		CSR_READ_1(sc, RL_EECMD) & ~x)
+
+/*
+ * Send a read command and address to the EEPROM, check for ACK.
+ */
+static void
+rl_eeprom_putbyte(struct rl_softc *sc, int addr)
+{
+	register int		d, i;
+
+	d = addr | sc->rl_eecmd_read;
+
+	/*
+	 * Feed in each bit and strobe the clock.
+	 */
+	for (i = 0x400; i; i >>= 1) {
+		if (d & i) {
+			EE_SET(RL_EE_DATAIN);
+		} else {
+			EE_CLR(RL_EE_DATAIN);
+		}
+		DELAY(100);
+		EE_SET(RL_EE_CLK);
+		DELAY(150);
+		EE_CLR(RL_EE_CLK);
+		DELAY(100);
+	}
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void
+rl_eeprom_getword(struct rl_softc *sc, int addr, uint16_t *dest)
+{
+	register int		i;
+	uint16_t		word = 0;
+
+	/* Enter EEPROM access mode. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);
+
+	/*
+	 * Send address of word we want to read.
+	 */
+	rl_eeprom_putbyte(sc, addr);
+
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);
+
+	/*
+	 * Start reading bits from EEPROM.
+	 */
+	for (i = 0x8000; i; i >>= 1) {
+		EE_SET(RL_EE_CLK);
+		DELAY(100);
+		if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT)
+			word |= i;
+		EE_CLR(RL_EE_CLK);
+		DELAY(100);
+	}
+
+	/* Turn off EEPROM access mode. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	*dest = word;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void
+rl_read_eeprom(struct rl_softc *sc, uint8_t *dest, int off, int cnt, int swap)
+{
+	int			i;
+	uint16_t		word = 0, *ptr;
+
+	for (i = 0; i < cnt; i++) {
+		rl_eeprom_getword(sc, off + i, &word);
+		ptr = (uint16_t *)(dest + (i * 2));
+		if (swap)
+			*ptr = ntohs(word);
+		else
+			*ptr = word;
+	}
+}
+
+/*
+ * Read the MII serial port for the MII bit-bang module.
+ */
+static uint32_t
+rl_mii_bitbang_read(device_t dev)
+{
+	struct rl_softc *sc;
+	uint32_t val;
+
+	sc = device_get_softc(dev);
+
+	val = CSR_READ_1(sc, RL_MII);
+	CSR_BARRIER(sc, RL_MII, 1,
+	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+
+	return (val);
+}
+
+/*
+ * Write the MII serial port for the MII bit-bang module.
+ */
+static void
+rl_mii_bitbang_write(device_t dev, uint32_t val)
+{
+	struct rl_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	CSR_WRITE_1(sc, RL_MII, val);
+	CSR_BARRIER(sc, RL_MII, 1,
+	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+}
+
+static int
+rl_miibus_readreg(device_t dev, int phy, int reg)
+{
+	struct rl_softc		*sc;
+	uint16_t		rl8139_reg;
+
+	sc = device_get_softc(dev);
+
+	if (sc->rl_type == RL_8139) {
+		switch (reg) {
+		case MII_BMCR:
+			rl8139_reg = RL_BMCR;
+			break;
+		case MII_BMSR:
+			rl8139_reg = RL_BMSR;
+			break;
+		case MII_ANAR:
+			rl8139_reg = RL_ANAR;
+			break;
+		case MII_ANER:
+			rl8139_reg = RL_ANER;
+			break;
+		case MII_ANLPAR:
+			rl8139_reg = RL_LPAR;
+			break;
+		case MII_PHYIDR1:
+		case MII_PHYIDR2:
+			return (0);
+		/*
+		 * Allow the rlphy driver to read the media status
+		 * register. If we have a link partner which does not
+		 * support NWAY, this is the register which will tell
+		 * us the results of parallel detection.
+		 */
+		case RL_MEDIASTAT:
+			return (CSR_READ_1(sc, RL_MEDIASTAT));
+		default:
+			device_printf(sc->rl_dev, "bad phy register\n");
+			return (0);
+		}
+		return (CSR_READ_2(sc, rl8139_reg));
+	}
+
+	return (mii_bitbang_readreg(dev, &rl_mii_bitbang_ops, phy, reg));
+}
+
+static int
+rl_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+	struct rl_softc		*sc;
+	uint16_t		rl8139_reg;
+
+	sc = device_get_softc(dev);
+
+	if (sc->rl_type == RL_8139) {
+		switch (reg) {
+		case MII_BMCR:
+			rl8139_reg = RL_BMCR;
+			break;
+		case MII_BMSR:
+			rl8139_reg = RL_BMSR;
+			break;
+		case MII_ANAR:
+			rl8139_reg = RL_ANAR;
+			break;
+		case MII_ANER:
+			rl8139_reg = RL_ANER;
+			break;
+		case MII_ANLPAR:
+			rl8139_reg = RL_LPAR;
+			break;
+		case MII_PHYIDR1:
+		case MII_PHYIDR2:
+			return (0);
+			break;
+		default:
+			device_printf(sc->rl_dev, "bad phy register\n");
+			return (0);
+		}
+		CSR_WRITE_2(sc, rl8139_reg, data);
+		return (0);
+	}
+
+	mii_bitbang_writereg(dev, &rl_mii_bitbang_ops, phy, reg, data);
+
+	return (0);
+}
+
+static void
+rl_miibus_statchg(device_t dev)
+{
+	struct rl_softc		*sc;
+	struct ifnet		*ifp;
+	struct mii_data		*mii;
+
+	sc = device_get_softc(dev);
+	mii = device_get_softc(sc->rl_miibus);
+	ifp = sc->rl_ifp;
+	if (mii == NULL || ifp == NULL ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		return;
+
+	sc->rl_flags &= ~RL_FLAG_LINK;
+	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+	    (IFM_ACTIVE | IFM_AVALID)) {
+		switch (IFM_SUBTYPE(mii->mii_media_active)) {
+		case IFM_10_T:
+		case IFM_100_TX:
+			sc->rl_flags |= RL_FLAG_LINK;
+			break;
+		default:
+			break;
+		}
+	}
+	/*
+	 * RealTek controllers do not provide any interface to
+	 * Tx/Rx MACs for resolved speed, duplex and flow-control
+	 * parameters.
+	 */
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+static void
+rl_rxfilter(struct rl_softc *sc)
+{
+	struct ifnet		*ifp = sc->rl_ifp;
+	int			h = 0;
+	uint32_t		hashes[2] = { 0, 0 };
+	struct ifmultiaddr	*ifma;
+	uint32_t		rxfilt;
+
+	RL_LOCK_ASSERT(sc);
+
+	rxfilt = CSR_READ_4(sc, RL_RXCFG);
+	rxfilt &= ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_BROAD |
+	    RL_RXCFG_RX_MULTI);
+	/* Always accept frames destined for this host. */
+	rxfilt |= RL_RXCFG_RX_INDIV;
+	/* Set capture broadcast bit to capture broadcast frames. */
+	if (ifp->if_flags & IFF_BROADCAST)
+		rxfilt |= RL_RXCFG_RX_BROAD;
+	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+		rxfilt |= RL_RXCFG_RX_MULTI;
+		if (ifp->if_flags & IFF_PROMISC)
+			rxfilt |= RL_RXCFG_RX_ALLPHYS;
+		hashes[0] = 0xFFFFFFFF;
+		hashes[1] = 0xFFFFFFFF;
+	} else {
+		/* Now program new ones. */
+		if_maddr_rlock(ifp);
+		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+			if (ifma->ifma_addr->sa_family != AF_LINK)
+				continue;
+			h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+			    ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+			if (h < 32)
+				hashes[0] |= (1 << h);
+			else
+				hashes[1] |= (1 << (h - 32));
+		}
+		if_maddr_runlock(ifp);
+		if (hashes[0] != 0 || hashes[1] != 0)
+			rxfilt |= RL_RXCFG_RX_MULTI;
+	}
+
+	CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
+	CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
+	CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
+}
+
+static void
+rl_reset(struct rl_softc *sc)
+{
+	register int		i;
+
+	RL_LOCK_ASSERT(sc);
+
+	CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET);
+
+	for (i = 0; i < RL_TIMEOUT; i++) {
+		DELAY(10);
+		if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET))
+			break;
+	}
+	if (i == RL_TIMEOUT)
+		device_printf(sc->rl_dev, "reset never completed!\n");
+}
+
+/*
+ * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device
+ * IDs against our list and return a device name if we find a match.
+ */
+static int
+rl_probe(device_t dev)
+{
+	const struct rl_type	*t;
+	uint16_t		devid, revid, vendor;
+	int			i;
+	
+	vendor = pci_get_vendor(dev);
+	devid = pci_get_device(dev);
+	revid = pci_get_revid(dev);
+
+	if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) {
+		if (revid == 0x20) {
+			/* 8139C+, let re(4) take care of this device. */
+			return (ENXIO);
+		}
+	}
+	t = rl_devs;
+	for (i = 0; i < sizeof(rl_devs) / sizeof(rl_devs[0]); i++, t++) {
+		if (vendor == t->rl_vid && devid == t->rl_did) {
+			device_set_desc(dev, t->rl_name);
+			return (BUS_PROBE_DEFAULT);
+		}
+	}
+
+	return (ENXIO);
+}
+
+struct rl_dmamap_arg {
+	bus_addr_t	rl_busaddr;
+};
+
+static void
+rl_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+	struct rl_dmamap_arg	*ctx;
+
+	if (error != 0)
+		return;
+
+	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
+
+        ctx = (struct rl_dmamap_arg *)arg;
+        ctx->rl_busaddr = segs[0].ds_addr;
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+rl_attach(device_t dev)
+{
+	uint8_t			eaddr[ETHER_ADDR_LEN];
+	uint16_t		as[3];
+	struct ifnet		*ifp;
+	struct rl_softc		*sc;
+	const struct rl_type	*t;
+	struct sysctl_ctx_list	*ctx;
+	struct sysctl_oid_list	*children;
+	int			error = 0, hwrev, i, phy, pmc, rid;
+	int			prefer_iomap, unit;
+	uint16_t		rl_did = 0;
+	char			tn[32];
+
+	sc = device_get_softc(dev);
+	unit = device_get_unit(dev);
+	sc->rl_dev = dev;
+
+	sc->rl_twister_enable = 0;
+	snprintf(tn, sizeof(tn), "dev.rl.%d.twister_enable", unit);
+	TUNABLE_INT_FETCH(tn, &sc->rl_twister_enable);
+	ctx = device_get_sysctl_ctx(sc->rl_dev);
+	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev));
+	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "twister_enable", CTLFLAG_RD,
+	   &sc->rl_twister_enable, 0, "");
+
+	mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+	    MTX_DEF);
+	callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0);
+
+	pci_enable_busmaster(dev);
+
+
+	/*
+	 * Map control/status registers.
+	 * Default to using PIO access for this driver. On SMP systems,
+	 * there appear to be problems with memory mapped mode: it looks
+	 * like doing too many memory mapped access back to back in rapid
+	 * succession can hang the bus. I'm inclined to blame this on
+	 * crummy design/construction on the part of RealTek. Memory
+	 * mapped mode does appear to work on uniprocessor systems though.
+	 */
+	prefer_iomap = 1;
+	snprintf(tn, sizeof(tn), "dev.rl.%d.prefer_iomap", unit);
+	TUNABLE_INT_FETCH(tn, &prefer_iomap);
+	if (prefer_iomap) {
+		sc->rl_res_id = PCIR_BAR(0);
+		sc->rl_res_type = SYS_RES_IOPORT;
+		sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type,
+		    &sc->rl_res_id, RF_ACTIVE);
+	}
+	if (prefer_iomap == 0 || sc->rl_res == NULL) {
+		sc->rl_res_id = PCIR_BAR(1);
+		sc->rl_res_type = SYS_RES_MEMORY;
+		sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type,
+		    &sc->rl_res_id, RF_ACTIVE);
+	}
+	if (sc->rl_res == NULL) {
+		device_printf(dev, "couldn't map ports/memory\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+#ifdef notdef
+	/*
+	 * Detect the Realtek 8139B. For some reason, this chip is very
+	 * unstable when left to autoselect the media
+	 * The best workaround is to set the device to the required
+	 * media type or to set it to the 10 Meg speed.
+	 */
+	if ((rman_get_end(sc->rl_res) - rman_get_start(sc->rl_res)) == 0xFF)
+		device_printf(dev,
+"Realtek 8139B detected. Warning, this may be unstable in autoselect mode\n");
+#endif
+
+	sc->rl_btag = rman_get_bustag(sc->rl_res);
+	sc->rl_bhandle = rman_get_bushandle(sc->rl_res);
+
+	/* Allocate interrupt */
+	rid = 0;
+	sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_SHAREABLE | RF_ACTIVE);
+
+	if (sc->rl_irq[0] == NULL) {
+		device_printf(dev, "couldn't map interrupt\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	sc->rl_cfg0 = RL_8139_CFG0;
+	sc->rl_cfg1 = RL_8139_CFG1;
+	sc->rl_cfg2 = 0;
+	sc->rl_cfg3 = RL_8139_CFG3;
+	sc->rl_cfg4 = RL_8139_CFG4;
+	sc->rl_cfg5 = RL_8139_CFG5;
+
+	/*
+	 * Reset the adapter. Only take the lock here as it's needed in
+	 * order to call rl_reset().
+	 */
+	RL_LOCK(sc);
+	rl_reset(sc);
+	RL_UNLOCK(sc);
+
+	sc->rl_eecmd_read = RL_EECMD_READ_6BIT;
+	rl_read_eeprom(sc, (uint8_t *)&rl_did, 0, 1, 0);
+	if (rl_did != 0x8129)
+		sc->rl_eecmd_read = RL_EECMD_READ_8BIT;
+
+	/*
+	 * Get station address from the EEPROM.
+	 */
+	rl_read_eeprom(sc, (uint8_t *)as, RL_EE_EADDR, 3, 0);
+	for (i = 0; i < 3; i++) {
+		eaddr[(i * 2) + 0] = as[i] & 0xff;
+		eaddr[(i * 2) + 1] = as[i] >> 8;
+	}
+
+	/*
+	 * Now read the exact device type from the EEPROM to find
+	 * out if it's an 8129 or 8139.
+	 */
+	rl_read_eeprom(sc, (uint8_t *)&rl_did, RL_EE_PCI_DID, 1, 0);
+
+	t = rl_devs;
+	sc->rl_type = 0;
+	while(t->rl_name != NULL) {
+		if (rl_did == t->rl_did) {
+			sc->rl_type = t->rl_basetype;
+			break;
+		}
+		t++;
+	}
+
+	if (sc->rl_type == 0) {
+		device_printf(dev, "unknown device ID: %x assuming 8139\n",
+		    rl_did);
+		sc->rl_type = RL_8139;
+		/*
+		 * Read RL_IDR register to get ethernet address as accessing
+		 * EEPROM may not extract correct address.
+		 */
+		for (i = 0; i < ETHER_ADDR_LEN; i++)
+			eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i);
+	}
+
+	if ((error = rl_dma_alloc(sc)) != 0)
+		goto fail;
+
+	ifp = sc->rl_ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		device_printf(dev, "can not if_alloc()\n");
+		error = ENOSPC;
+		goto fail;
+	}
+
+#define	RL_PHYAD_INTERNAL	0
+
+	/* Do MII setup */
+	phy = MII_PHY_ANY;
+	if (sc->rl_type == RL_8139)
+		phy = RL_PHYAD_INTERNAL;
+	error = mii_attach(dev, &sc->rl_miibus, ifp, rl_ifmedia_upd,
+	    rl_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
+	if (error != 0) {
+		device_printf(dev, "attaching PHYs failed\n");
+		goto fail;
+	}
+
+	ifp->if_softc = sc;
+	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+	ifp->if_mtu = ETHERMTU;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_ioctl = rl_ioctl;
+	ifp->if_start = rl_start;
+	ifp->if_init = rl_init;
+	ifp->if_capabilities = IFCAP_VLAN_MTU;
+	/* Check WOL for RTL8139B or newer controllers. */
+	if (sc->rl_type == RL_8139 &&
+	    pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
+		hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
+		switch (hwrev) {
+		case RL_HWREV_8139B:
+		case RL_HWREV_8130:
+		case RL_HWREV_8139C:
+		case RL_HWREV_8139D:
+		case RL_HWREV_8101:
+		case RL_HWREV_8100:
+			ifp->if_capabilities |= IFCAP_WOL;
+			/* Disable WOL. */
+			rl_clrwol(sc);
+			break;
+		default:
+			break;
+		}
+	}
+	ifp->if_capenable = ifp->if_capabilities;
+	ifp->if_capenable &= ~(IFCAP_WOL_UCAST | IFCAP_WOL_MCAST);
+#ifdef DEVICE_POLLING
+	ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+	IFQ_SET_READY(&ifp->if_snd);
+
+	/*
+	 * Call MI attach routine.
+	 */
+	ether_ifattach(ifp, eaddr);
+
+	/* Hook interrupt last to avoid having to lock softc */
+	error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE,
+	    NULL, rl_intr, sc, &sc->rl_intrhand[0]);
+	if (error) {
+		device_printf(sc->rl_dev, "couldn't set up irq\n");
+		ether_ifdetach(ifp);
+	}
+
+fail:
+	if (error)
+		rl_detach(dev);
+
+	return (error);
+}
+
+/*
+ * Shutdown hardware and free up resources. This can be called any
+ * time after the mutex has been initialized. It is called in both
+ * the error case in attach and the normal detach case so it needs
+ * to be careful about only freeing resources that have actually been
+ * allocated.
+ */
+static int
+rl_detach(device_t dev)
+{
+	struct rl_softc		*sc;
+	struct ifnet		*ifp;
+
+	sc = device_get_softc(dev);
+	ifp = sc->rl_ifp;
+
+	KASSERT(mtx_initialized(&sc->rl_mtx), ("rl mutex not initialized"));
+
+#ifdef DEVICE_POLLING
+	if (ifp->if_capenable & IFCAP_POLLING)
+		ether_poll_deregister(ifp);
+#endif
+	/* These should only be active if attach succeeded */
+	if (device_is_attached(dev)) {
+		RL_LOCK(sc);
+		rl_stop(sc);
+		RL_UNLOCK(sc);
+		callout_drain(&sc->rl_stat_callout);
+		ether_ifdetach(ifp);
+	}
+#if 0
+	sc->suspended = 1;
+#endif
+	if (sc->rl_miibus)
+		device_delete_child(dev, sc->rl_miibus);
+	bus_generic_detach(dev);
+
+	if (sc->rl_intrhand[0])
+		bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]);
+	if (sc->rl_irq[0])
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq[0]);
+	if (sc->rl_res)
+		bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id,
+		    sc->rl_res);
+
+	if (ifp)
+		if_free(ifp);
+
+	rl_dma_free(sc);
+
+	mtx_destroy(&sc->rl_mtx);
+
+	return (0);
+}
+
+static int
+rl_dma_alloc(struct rl_softc *sc)
+{
+	struct rl_dmamap_arg	ctx;
+	int			error, i;
+
+	/*
+	 * Allocate the parent bus DMA tag appropriate for PCI.
+	 */
+	error = bus_dma_tag_create(bus_get_dma_tag(sc->rl_dev),	/* parent */
+	    1, 0,			/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsize, nsegments */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->rl_parent_tag);
+	if (error) {
+                device_printf(sc->rl_dev,
+		    "failed to create parent DMA tag.\n");
+		goto fail;
+	}
+	/* Create DMA tag for Rx memory block. */
+	error = bus_dma_tag_create(sc->rl_parent_tag,	/* parent */
+	    RL_RX_8139_BUF_ALIGN, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, 1,	/* maxsize,nsegments */
+	    RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ,	/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->rl_cdata.rl_rx_tag);
+	if (error) {
+                device_printf(sc->rl_dev,
+		    "failed to create Rx memory block DMA tag.\n");
+		goto fail;
+	}
+	/* Create DMA tag for Tx buffer. */
+	error = bus_dma_tag_create(sc->rl_parent_tag,	/* parent */
+	    RL_TX_8139_BUF_ALIGN, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    MCLBYTES, 1,		/* maxsize, nsegments */
+	    MCLBYTES,			/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->rl_cdata.rl_tx_tag);
+	if (error) {
+                device_printf(sc->rl_dev, "failed to create Tx DMA tag.\n");
+		goto fail;
+	}
+
+	/*
+	 * Allocate DMA'able memory and load DMA map for Rx memory block.
+	 */
+	error = bus_dmamem_alloc(sc->rl_cdata.rl_rx_tag,
+	    (void **)&sc->rl_cdata.rl_rx_buf, BUS_DMA_WAITOK |
+	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_cdata.rl_rx_dmamap);
+	if (error != 0) {
+		device_printf(sc->rl_dev,
+		    "failed to allocate Rx DMA memory block.\n");
+		goto fail;
+	}
+	ctx.rl_busaddr = 0;
+	error = bus_dmamap_load(sc->rl_cdata.rl_rx_tag,
+	    sc->rl_cdata.rl_rx_dmamap, sc->rl_cdata.rl_rx_buf,
+	    RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, rl_dmamap_cb, &ctx,
+	    BUS_DMA_NOWAIT);
+	if (error != 0 || ctx.rl_busaddr == 0) {
+		device_printf(sc->rl_dev,
+		    "could not load Rx DMA memory block.\n");
+		goto fail;
+	}
+	sc->rl_cdata.rl_rx_buf_paddr = ctx.rl_busaddr;
+
+	/* Create DMA maps for Tx buffers. */
+	for (i = 0; i < RL_TX_LIST_CNT; i++) {
+		sc->rl_cdata.rl_tx_chain[i] = NULL;
+		sc->rl_cdata.rl_tx_dmamap[i] = NULL;
+		error = bus_dmamap_create(sc->rl_cdata.rl_tx_tag, 0,
+		    &sc->rl_cdata.rl_tx_dmamap[i]);
+		if (error != 0) {
+			device_printf(sc->rl_dev,
+			    "could not create Tx dmamap.\n");
+			goto fail;
+		}
+	}
+
+	/* Leave a few bytes before the start of the RX ring buffer. */
+	sc->rl_cdata.rl_rx_buf_ptr = sc->rl_cdata.rl_rx_buf;
+	sc->rl_cdata.rl_rx_buf += RL_RX_8139_BUF_RESERVE;
+
+fail:
+	return (error);
+}
+
+static void
+rl_dma_free(struct rl_softc *sc)
+{
+	int			i;
+
+	/* Rx memory block. */
+	if (sc->rl_cdata.rl_rx_tag != NULL) {
+		if (sc->rl_cdata.rl_rx_dmamap != NULL)
+			bus_dmamap_unload(sc->rl_cdata.rl_rx_tag,
+			    sc->rl_cdata.rl_rx_dmamap);
+		if (sc->rl_cdata.rl_rx_dmamap != NULL &&
+		    sc->rl_cdata.rl_rx_buf_ptr != NULL)
+			bus_dmamem_free(sc->rl_cdata.rl_rx_tag,
+			    sc->rl_cdata.rl_rx_buf_ptr,
+			    sc->rl_cdata.rl_rx_dmamap);
+		sc->rl_cdata.rl_rx_buf_ptr = NULL;
+		sc->rl_cdata.rl_rx_buf = NULL;
+		sc->rl_cdata.rl_rx_dmamap = NULL;
+		bus_dma_tag_destroy(sc->rl_cdata.rl_rx_tag);
+		sc->rl_cdata.rl_tx_tag = NULL;
+	}
+
+	/* Tx buffers. */
+	if (sc->rl_cdata.rl_tx_tag != NULL) {
+		for (i = 0; i < RL_TX_LIST_CNT; i++) {
+			if (sc->rl_cdata.rl_tx_dmamap[i] != NULL) {
+				bus_dmamap_destroy(
+				    sc->rl_cdata.rl_tx_tag,
+				    sc->rl_cdata.rl_tx_dmamap[i]);
+				sc->rl_cdata.rl_tx_dmamap[i] = NULL;
+			}
+		}
+		bus_dma_tag_destroy(sc->rl_cdata.rl_tx_tag);
+		sc->rl_cdata.rl_tx_tag = NULL;
+	}
+
+	if (sc->rl_parent_tag != NULL) {
+		bus_dma_tag_destroy(sc->rl_parent_tag);
+		sc->rl_parent_tag = NULL;
+	}
+}
+
+/*
+ * Initialize the transmit descriptors.
+ */
+static int
+rl_list_tx_init(struct rl_softc *sc)
+{
+	struct rl_chain_data	*cd;
+	int			i;
+
+	RL_LOCK_ASSERT(sc);
+
+	cd = &sc->rl_cdata;
+	for (i = 0; i < RL_TX_LIST_CNT; i++) {
+		cd->rl_tx_chain[i] = NULL;
+		CSR_WRITE_4(sc,
+		    RL_TXADDR0 + (i * sizeof(uint32_t)), 0x0000000);
+	}
+
+	sc->rl_cdata.cur_tx = 0;
+	sc->rl_cdata.last_tx = 0;
+
+	return (0);
+}
+
+static int
+rl_list_rx_init(struct rl_softc *sc)
+{
+
+	RL_LOCK_ASSERT(sc);
+
+	bzero(sc->rl_cdata.rl_rx_buf_ptr,
+	    RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ);
+	bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_rx_dmamap,
+	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+	return (0);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ *
+ * You know there's something wrong with a PCI bus-master chip design
+ * when you have to use m_devget().
+ *
+ * The receive operation is badly documented in the datasheet, so I'll
+ * attempt to document it here. The driver provides a buffer area and
+ * places its base address in the RX buffer start address register.
+ * The chip then begins copying frames into the RX buffer. Each frame
+ * is preceded by a 32-bit RX status word which specifies the length
+ * of the frame and certain other status bits. Each frame (starting with
+ * the status word) is also 32-bit aligned. The frame length is in the
+ * first 16 bits of the status word; the lower 15 bits correspond with
+ * the 'rx status register' mentioned in the datasheet.
+ *
+ * Note: to make the Alpha happy, the frame payload needs to be aligned
+ * on a 32-bit boundary. To achieve this, we pass RL_ETHER_ALIGN (2 bytes)
+ * as the offset argument to m_devget().
+ */
+static int
+rl_rxeof(struct rl_softc *sc)
+{
+	struct mbuf		*m;
+	struct ifnet		*ifp = sc->rl_ifp;
+	uint8_t			*rxbufpos;
+	int			total_len = 0;
+	int			wrap = 0;
+	int			rx_npkts = 0;
+	uint32_t		rxstat;
+	uint16_t		cur_rx;
+	uint16_t		limit;
+	uint16_t		max_bytes, rx_bytes = 0;
+
+	RL_LOCK_ASSERT(sc);
+
+	bus_dmamap_sync(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_dmamap,
+	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+	cur_rx = (CSR_READ_2(sc, RL_CURRXADDR) + 16) % RL_RXBUFLEN;
+
+	/* Do not try to read past this point. */
+	limit = CSR_READ_2(sc, RL_CURRXBUF) % RL_RXBUFLEN;
+
+	if (limit < cur_rx)
+		max_bytes = (RL_RXBUFLEN - cur_rx) + limit;
+	else
+		max_bytes = limit - cur_rx;
+
+	while((CSR_READ_1(sc, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) {
+#ifdef DEVICE_POLLING
+		if (ifp->if_capenable & IFCAP_POLLING) {
+			if (sc->rxcycles <= 0)
+				break;
+			sc->rxcycles--;
+		}
+#endif
+		rxbufpos = sc->rl_cdata.rl_rx_buf + cur_rx;
+		rxstat = le32toh(*(uint32_t *)rxbufpos);
+
+		/*
+		 * Here's a totally undocumented fact for you. When the
+		 * RealTek chip is in the process of copying a packet into
+		 * RAM for you, the length will be 0xfff0. If you spot a
+		 * packet header with this value, you need to stop. The
+		 * datasheet makes absolutely no mention of this and
+		 * RealTek should be shot for this.
+		 */
+		total_len = rxstat >> 16;
+		if (total_len == RL_RXSTAT_UNFINISHED)
+			break;
+
+		if (!(rxstat & RL_RXSTAT_RXOK) ||
+		    total_len < ETHER_MIN_LEN ||
+		    total_len > ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) {
+			ifp->if_ierrors++;
+			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+			rl_init_locked(sc);
+			return (rx_npkts);
+		}
+
+		/* No errors; receive the packet. */
+		rx_bytes += total_len + 4;
+
+		/*
+		 * XXX The RealTek chip includes the CRC with every
+		 * received frame, and there's no way to turn this
+		 * behavior off (at least, I can't find anything in
+		 * the manual that explains how to do it) so we have
+		 * to trim off the CRC manually.
+		 */
+		total_len -= ETHER_CRC_LEN;
+
+		/*
+		 * Avoid trying to read more bytes than we know
+		 * the chip has prepared for us.
+		 */
+		if (rx_bytes > max_bytes)
+			break;
+
+		rxbufpos = sc->rl_cdata.rl_rx_buf +
+			((cur_rx + sizeof(uint32_t)) % RL_RXBUFLEN);
+		if (rxbufpos == (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN))
+			rxbufpos = sc->rl_cdata.rl_rx_buf;
+
+		wrap = (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN) - rxbufpos;
+		if (total_len > wrap) {
+			m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp,
+			    NULL);
+			if (m != NULL)
+				m_copyback(m, wrap, total_len - wrap,
+					sc->rl_cdata.rl_rx_buf);
+			cur_rx = (total_len - wrap + ETHER_CRC_LEN);
+		} else {
+			m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp,
+			    NULL);
+			cur_rx += total_len + 4 + ETHER_CRC_LEN;
+		}
+
+		/* Round up to 32-bit boundary. */
+		cur_rx = (cur_rx + 3) & ~3;
+		CSR_WRITE_2(sc, RL_CURRXADDR, cur_rx - 16);
+
+		if (m == NULL) {
+			ifp->if_iqdrops++;
+			continue;
+		}
+
+		ifp->if_ipackets++;
+		RL_UNLOCK(sc);
+		(*ifp->if_input)(ifp, m);
+		RL_LOCK(sc);
+		rx_npkts++;
+	}
+
+	/* No need to sync Rx memory block as we didn't modify it. */
+	return (rx_npkts);
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+static void
+rl_txeof(struct rl_softc *sc)
+{
+	struct ifnet		*ifp = sc->rl_ifp;
+	uint32_t		txstat;
+
+	RL_LOCK_ASSERT(sc);
+
+	/*
+	 * Go through our tx list and free mbufs for those
+	 * frames that have been uploaded.
+	 */
+	do {
+		if (RL_LAST_TXMBUF(sc) == NULL)
+			break;
+		txstat = CSR_READ_4(sc, RL_LAST_TXSTAT(sc));
+		if (!(txstat & (RL_TXSTAT_TX_OK|
+		    RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT)))
+			break;
+
+		ifp->if_collisions += (txstat & RL_TXSTAT_COLLCNT) >> 24;
+
+		bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc),
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc));
+		m_freem(RL_LAST_TXMBUF(sc));
+		RL_LAST_TXMBUF(sc) = NULL;
+		/*
+		 * If there was a transmit underrun, bump the TX threshold.
+		 * Make sure not to overflow the 63 * 32byte we can address
+		 * with the 6 available bit.
+		 */
+		if ((txstat & RL_TXSTAT_TX_UNDERRUN) &&
+		    (sc->rl_txthresh < 2016))
+			sc->rl_txthresh += 32;
+		if (txstat & RL_TXSTAT_TX_OK)
+			ifp->if_opackets++;
+		else {
+			int			oldthresh;
+			ifp->if_oerrors++;
+			if ((txstat & RL_TXSTAT_TXABRT) ||
+			    (txstat & RL_TXSTAT_OUTOFWIN))
+				CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG);
+			oldthresh = sc->rl_txthresh;
+			/* error recovery */
+			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+			rl_init_locked(sc);
+			/* restore original threshold */
+			sc->rl_txthresh = oldthresh;
+			return;
+		}
+		RL_INC(sc->rl_cdata.last_tx);
+		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	} while (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx);
+
+	if (RL_LAST_TXMBUF(sc) == NULL)
+		sc->rl_watchdog_timer = 0;
+}
+
+static void
+rl_twister_update(struct rl_softc *sc)
+{
+	uint16_t linktest;
+	/*
+	 * Table provided by RealTek (Kinston <shangh at realtek.com.tw>) for
+	 * Linux driver.  Values undocumented otherwise.
+	 */
+	static const uint32_t param[4][4] = {
+		{0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+		{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+		{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+		{0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+	};
+
+	/*
+	 * Tune the so-called twister registers of the RTL8139.  These
+	 * are used to compensate for impedance mismatches.  The
+	 * method for tuning these registers is undocumented and the
+	 * following procedure is collected from public sources.
+	 */
+	switch (sc->rl_twister)
+	{
+	case CHK_LINK:
+		/*
+		 * If we have a sufficient link, then we can proceed in
+		 * the state machine to the next stage.  If not, then
+		 * disable further tuning after writing sane defaults.
+		 */
+		if (CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_LINK_OK) {
+			CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_OFF_CMD);
+			sc->rl_twister = FIND_ROW;
+		} else {
+			CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_CMD);
+			CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST);
+			CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF);
+			CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF);
+			sc->rl_twister = DONE;
+		}
+		break;
+	case FIND_ROW:
+		/*
+		 * Read how long it took to see the echo to find the tuning
+		 * row to use.
+		 */
+		linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS;
+		if (linktest == RL_CSCFG_ROW3)
+			sc->rl_twist_row = 3;
+		else if (linktest == RL_CSCFG_ROW2)
+			sc->rl_twist_row = 2;
+		else if (linktest == RL_CSCFG_ROW1)
+			sc->rl_twist_row = 1;
+		else
+			sc->rl_twist_row = 0;
+		sc->rl_twist_col = 0;
+		sc->rl_twister = SET_PARAM;
+		break;
+	case SET_PARAM:
+		if (sc->rl_twist_col == 0)
+			CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET);
+		CSR_WRITE_4(sc, RL_PARA7C,
+		    param[sc->rl_twist_row][sc->rl_twist_col]);
+		if (++sc->rl_twist_col == 4) {
+			if (sc->rl_twist_row == 3)
+				sc->rl_twister = RECHK_LONG;
+			else
+				sc->rl_twister = DONE;
+		}
+		break;
+	case RECHK_LONG:
+		/*
+		 * For long cables, we have to double check to make sure we
+		 * don't mistune.
+		 */
+		linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS;
+		if (linktest == RL_CSCFG_ROW3)
+			sc->rl_twister = DONE;
+		else {
+			CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_RETUNE);
+			sc->rl_twister = RETUNE;
+		}
+		break;
+	case RETUNE:
+		/* Retune for a shorter cable (try column 2) */
+		CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST);
+		CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF);
+		CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF);
+		CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET);
+		sc->rl_twist_row--;
+		sc->rl_twist_col = 0;
+		sc->rl_twister = SET_PARAM;
+		break;
+
+	case DONE:
+		break;
+	}
+	
+}
+
+static void
+rl_tick(void *xsc)
+{
+	struct rl_softc		*sc = xsc;
+	struct mii_data		*mii;
+	int ticks;
+
+	RL_LOCK_ASSERT(sc);
+	/*
+	 * If we're doing the twister cable calibration, then we need to defer
+	 * watchdog timeouts.  This is a no-op in normal operations, but
+	 * can falsely trigger when the cable calibration takes a while and
+	 * there was traffic ready to go when rl was started.
+	 *
+	 * We don't defer mii_tick since that updates the mii status, which
+	 * helps the twister process, at least according to similar patches
+	 * for the Linux driver I found online while doing the fixes.  Worst
+	 * case is a few extra mii reads during calibration.
+	 */
+	mii = device_get_softc(sc->rl_miibus);
+	mii_tick(mii);
+	if ((sc->rl_flags & RL_FLAG_LINK) == 0)
+		rl_miibus_statchg(sc->rl_dev);
+	if (sc->rl_twister_enable) {
+		if (sc->rl_twister == DONE)
+			rl_watchdog(sc);
+		else
+			rl_twister_update(sc);
+		if (sc->rl_twister == DONE)
+			ticks = hz;
+		else
+			ticks = hz / 10;
+	} else {
+		rl_watchdog(sc);
+		ticks = hz;
+	}
+
+	callout_reset(&sc->rl_stat_callout, ticks, rl_tick, sc);
+}
+
+#ifdef DEVICE_POLLING
+static int
+rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+	struct rl_softc *sc = ifp->if_softc;
+	int rx_npkts = 0;
+
+	RL_LOCK(sc);
+	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+		rx_npkts = rl_poll_locked(ifp, cmd, count);
+	RL_UNLOCK(sc);
+	return (rx_npkts);
+}
+
+static int
+rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+	struct rl_softc *sc = ifp->if_softc;
+	int rx_npkts;
+
+	RL_LOCK_ASSERT(sc);
+
+	sc->rxcycles = count;
+	rx_npkts = rl_rxeof(sc);
+	rl_txeof(sc);
+
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		rl_start_locked(ifp);
+
+	if (cmd == POLL_AND_CHECK_STATUS) {
+		uint16_t	status;
+
+		/* We should also check the status register. */
+		status = CSR_READ_2(sc, RL_ISR);
+		if (status == 0xffff)
+			return (rx_npkts);
+		if (status != 0)
+			CSR_WRITE_2(sc, RL_ISR, status);
+
+		/* XXX We should check behaviour on receiver stalls. */
+
+		if (status & RL_ISR_SYSTEM_ERR) {
+			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+			rl_init_locked(sc);
+		}
+	}
+	return (rx_npkts);
+}
+#endif /* DEVICE_POLLING */
+
+static void
+rl_intr(void *arg)
+{
+	struct rl_softc		*sc = arg;
+	struct ifnet		*ifp = sc->rl_ifp;
+	uint16_t		status;
+	int			count;
+
+	RL_LOCK(sc);
+
+	if (sc->suspended)
+		goto done_locked;
+
+#ifdef DEVICE_POLLING
+	if  (ifp->if_capenable & IFCAP_POLLING)
+		goto done_locked;
+#endif
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		goto done_locked2;
+	status = CSR_READ_2(sc, RL_ISR);
+	if (status == 0xffff || (status & RL_INTRS) == 0)
+		goto done_locked;
+	/*
+	 * Ours, disable further interrupts.
+	 */
+	CSR_WRITE_2(sc, RL_IMR, 0);
+	for (count = 16; count > 0; count--) {
+		CSR_WRITE_2(sc, RL_ISR, status);
+		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+			if (status & (RL_ISR_RX_OK | RL_ISR_RX_ERR))
+				rl_rxeof(sc);
+			if (status & (RL_ISR_TX_OK | RL_ISR_TX_ERR))
+				rl_txeof(sc);
+			if (status & RL_ISR_SYSTEM_ERR) {
+				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+				rl_init_locked(sc);
+				RL_UNLOCK(sc);
+				return;
+			}
+		}
+		status = CSR_READ_2(sc, RL_ISR);
+		/* If the card has gone away, the read returns 0xffff. */
+		if (status == 0xffff || (status & RL_INTRS) == 0)
+			break;
+	}
+
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		rl_start_locked(ifp);
+
+done_locked2:
+	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+		CSR_WRITE_2(sc, RL_IMR, RL_INTRS);
+done_locked:
+	RL_UNLOCK(sc);
+}
+
+/*
+ * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
+ * pointers to the fragment pointers.
+ */
+static int
+rl_encap(struct rl_softc *sc, struct mbuf **m_head)
+{
+	struct mbuf		*m;
+	bus_dma_segment_t	txsegs[1];
+	int			error, nsegs, padlen;
+
+	RL_LOCK_ASSERT(sc);
+
+	m = *m_head;
+	padlen = 0;
+	/*
+	 * Hardware doesn't auto-pad, so we have to make sure
+	 * pad short frames out to the minimum frame length.
+	 */
+	if (m->m_pkthdr.len < RL_MIN_FRAMELEN)
+		padlen = RL_MIN_FRAMELEN - m->m_pkthdr.len;
+	/*
+	 * The RealTek is brain damaged and wants longword-aligned
+	 * TX buffers, plus we can only have one fragment buffer
+	 * per packet. We have to copy pretty much all the time.
+	 */
+	if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0 ||
+	    (padlen > 0 && M_TRAILINGSPACE(m) < padlen)) {
+		m = m_defrag(*m_head, M_NOWAIT);
+		if (m == NULL) {
+			m_freem(*m_head);
+			*m_head = NULL;
+			return (ENOMEM);
+		}
+	}
+	*m_head = m;
+
+	if (padlen > 0) {
+		/*
+		 * Make security-conscious people happy: zero out the
+		 * bytes in the pad area, since we don't know what
+		 * this mbuf cluster buffer's previous user might
+		 * have left in it.
+		 */
+		bzero(mtod(m, char *) + m->m_pkthdr.len, padlen);
+		m->m_pkthdr.len += padlen;
+		m->m_len = m->m_pkthdr.len;
+	}
+
+	error = bus_dmamap_load_mbuf_sg(sc->rl_cdata.rl_tx_tag,
+	    RL_CUR_DMAMAP(sc), m, txsegs, &nsegs, 0);
+	if (error != 0)
+		return (error);
+	if (nsegs == 0) {
+		m_freem(*m_head);
+		*m_head = NULL;
+		return (EIO);
+	}
+
+	RL_CUR_TXMBUF(sc) = m;
+	bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_CUR_DMAMAP(sc),
+	    BUS_DMASYNC_PREWRITE);
+	CSR_WRITE_4(sc, RL_CUR_TXADDR(sc), RL_ADDR_LO(txsegs[0].ds_addr));
+
+	return (0);
+}
+
+/*
+ * Main transmit routine.
+ */
+static void
+rl_start(struct ifnet *ifp)
+{
+	struct rl_softc		*sc = ifp->if_softc;
+
+	RL_LOCK(sc);
+	rl_start_locked(ifp);
+	RL_UNLOCK(sc);
+}
+
+static void
+rl_start_locked(struct ifnet *ifp)
+{
+	struct rl_softc		*sc = ifp->if_softc;
+	struct mbuf		*m_head = NULL;
+
+	RL_LOCK_ASSERT(sc);
+
+	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+	    IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0)
+		return;
+
+	while (RL_CUR_TXMBUF(sc) == NULL) {
+
+		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+
+		if (m_head == NULL)
+			break;
+
+		if (rl_encap(sc, &m_head)) {
+			if (m_head == NULL)
+				break;
+			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+			break;
+		}
+
+		/* Pass a copy of this mbuf chain to the bpf subsystem. */
+		BPF_MTAP(ifp, RL_CUR_TXMBUF(sc));
+
+		/* Transmit the frame. */
+		CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc),
+		    RL_TXTHRESH(sc->rl_txthresh) |
+		    RL_CUR_TXMBUF(sc)->m_pkthdr.len);
+
+		RL_INC(sc->rl_cdata.cur_tx);
+
+		/* Set a timeout in case the chip goes out to lunch. */
+		sc->rl_watchdog_timer = 5;
+	}
+
+	/*
+	 * We broke out of the loop because all our TX slots are
+	 * full. Mark the NIC as busy until it drains some of the
+	 * packets from the queue.
+	 */
+	if (RL_CUR_TXMBUF(sc) != NULL)
+		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+}
+
+static void
+rl_init(void *xsc)
+{
+	struct rl_softc		*sc = xsc;
+
+	RL_LOCK(sc);
+	rl_init_locked(sc);
+	RL_UNLOCK(sc);
+}
+
+static void
+rl_init_locked(struct rl_softc *sc)
+{
+	struct ifnet		*ifp = sc->rl_ifp;
+	struct mii_data		*mii;
+	uint32_t		eaddr[2];
+
+	RL_LOCK_ASSERT(sc);
+
+	mii = device_get_softc(sc->rl_miibus);
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+		return;
+
+	/*
+	 * Cancel pending I/O and free all RX/TX buffers.
+	 */
+	rl_stop(sc);
+
+	rl_reset(sc);
+	if (sc->rl_twister_enable) {
+		/*
+		 * Reset twister register tuning state.  The twister
+		 * registers and their tuning are undocumented, but
+		 * are necessary to cope with bad links.  rl_twister =
+		 * DONE here will disable this entirely.
+		 */
+		sc->rl_twister = CHK_LINK;
+	}
+
+	/*
+	 * Init our MAC address.  Even though the chipset
+	 * documentation doesn't mention it, we need to enter "Config
+	 * register write enable" mode to modify the ID registers.
+	 */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
+	bzero(eaddr, sizeof(eaddr));
+	bcopy(IF_LLADDR(sc->rl_ifp), eaddr, ETHER_ADDR_LEN);
+	CSR_WRITE_STREAM_4(sc, RL_IDR0, eaddr[0]);
+	CSR_WRITE_STREAM_4(sc, RL_IDR4, eaddr[1]);
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	/* Init the RX memory block pointer register. */
+	CSR_WRITE_4(sc, RL_RXADDR, sc->rl_cdata.rl_rx_buf_paddr +
+	    RL_RX_8139_BUF_RESERVE);
+	/* Init TX descriptors. */
+	rl_list_tx_init(sc);
+	/* Init Rx memory block. */
+	rl_list_rx_init(sc);
+
+	/*
+	 * Enable transmit and receive.
+	 */
+	CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);
+
+	/*
+	 * Set the initial TX and RX configuration.
+	 */
+	CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG);
+	CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG);
+
+	/* Set RX filter. */
+	rl_rxfilter(sc);
+
+#ifdef DEVICE_POLLING
+	/* Disable interrupts if we are polling. */
+	if (ifp->if_capenable & IFCAP_POLLING)
+		CSR_WRITE_2(sc, RL_IMR, 0);
+	else
+#endif
+	/* Enable interrupts. */
+	CSR_WRITE_2(sc, RL_IMR, RL_INTRS);
+
+	/* Set initial TX threshold */
+	sc->rl_txthresh = RL_TX_THRESH_INIT;
+
+	/* Start RX/TX process. */
+	CSR_WRITE_4(sc, RL_MISSEDPKT, 0);
+
+	/* Enable receiver and transmitter. */
+	CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);
+
+	sc->rl_flags &= ~RL_FLAG_LINK;
+	mii_mediachg(mii);
+
+	CSR_WRITE_1(sc, sc->rl_cfg1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX);
+
+	ifp->if_drv_flags |= IFF_DRV_RUNNING;
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+	callout_reset(&sc->rl_stat_callout, hz, rl_tick, sc);
+}
+
+/*
+ * Set media options.
+ */
+static int
+rl_ifmedia_upd(struct ifnet *ifp)
+{
+	struct rl_softc		*sc = ifp->if_softc;
+	struct mii_data		*mii;
+
+	mii = device_get_softc(sc->rl_miibus);
+
+	RL_LOCK(sc);
+	mii_mediachg(mii);
+	RL_UNLOCK(sc);
+
+	return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+rl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct rl_softc		*sc = ifp->if_softc;
+	struct mii_data		*mii;
+
+	mii = device_get_softc(sc->rl_miibus);
+
+	RL_LOCK(sc);
+	mii_pollstat(mii);
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+	RL_UNLOCK(sc);
+}
+
+static int
+rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+	struct ifreq		*ifr = (struct ifreq *)data;
+	struct mii_data		*mii;
+	struct rl_softc		*sc = ifp->if_softc;
+	int			error = 0, mask;
+
+	switch (command) {
+	case SIOCSIFFLAGS:
+		RL_LOCK(sc);
+		if (ifp->if_flags & IFF_UP) {
+			if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+			    ((ifp->if_flags ^ sc->rl_if_flags) &
+                            (IFF_PROMISC | IFF_ALLMULTI)))
+				rl_rxfilter(sc);
+                        else
+				rl_init_locked(sc);
+                } else if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+			rl_stop(sc);
+		sc->rl_if_flags = ifp->if_flags;
+		RL_UNLOCK(sc);
+		break;
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		RL_LOCK(sc);
+		rl_rxfilter(sc);
+		RL_UNLOCK(sc);
+		break;
+	case SIOCGIFMEDIA:
+	case SIOCSIFMEDIA:
+		mii = device_get_softc(sc->rl_miibus);
+		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+		break;
+	case SIOCSIFCAP:
+		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+#ifdef DEVICE_POLLING
+		if (ifr->ifr_reqcap & IFCAP_POLLING &&
+		    !(ifp->if_capenable & IFCAP_POLLING)) {
+			error = ether_poll_register(rl_poll, ifp);
+			if (error)
+				return(error);
+			RL_LOCK(sc);
+			/* Disable interrupts */
+			CSR_WRITE_2(sc, RL_IMR, 0x0000);
+			ifp->if_capenable |= IFCAP_POLLING;
+			RL_UNLOCK(sc);
+			return (error);
+			
+		}
+		if (!(ifr->ifr_reqcap & IFCAP_POLLING) &&
+		    ifp->if_capenable & IFCAP_POLLING) {
+			error = ether_poll_deregister(ifp);
+			/* Enable interrupts. */
+			RL_LOCK(sc);
+			CSR_WRITE_2(sc, RL_IMR, RL_INTRS);
+			ifp->if_capenable &= ~IFCAP_POLLING;
+			RL_UNLOCK(sc);
+			return (error);
+		}
+#endif /* DEVICE_POLLING */
+		if ((mask & IFCAP_WOL) != 0 &&
+		    (ifp->if_capabilities & IFCAP_WOL) != 0) {
+			if ((mask & IFCAP_WOL_UCAST) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_UCAST;
+			if ((mask & IFCAP_WOL_MCAST) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_MCAST;
+			if ((mask & IFCAP_WOL_MAGIC) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_MAGIC;
+		}
+		break;
+	default:
+		error = ether_ioctl(ifp, command, data);
+		break;
+	}
+
+	return (error);
+}
+
+static void
+rl_watchdog(struct rl_softc *sc)
+{
+
+	RL_LOCK_ASSERT(sc);
+
+	if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer >0)
+		return;
+
+	device_printf(sc->rl_dev, "watchdog timeout\n");
+	sc->rl_ifp->if_oerrors++;
+
+	rl_txeof(sc);
+	rl_rxeof(sc);
+	sc->rl_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+	rl_init_locked(sc);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+rl_stop(struct rl_softc *sc)
+{
+	register int		i;
+	struct ifnet		*ifp = sc->rl_ifp;
+
+	RL_LOCK_ASSERT(sc);
+
+	sc->rl_watchdog_timer = 0;
+	callout_stop(&sc->rl_stat_callout);
+	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+	sc->rl_flags &= ~RL_FLAG_LINK;
+
+	CSR_WRITE_1(sc, RL_COMMAND, 0x00);
+	CSR_WRITE_2(sc, RL_IMR, 0x0000);
+	for (i = 0; i < RL_TIMEOUT; i++) {
+		DELAY(10);
+		if ((CSR_READ_1(sc, RL_COMMAND) &
+		    (RL_CMD_RX_ENB | RL_CMD_TX_ENB)) == 0)
+			break;
+	}
+	if (i == RL_TIMEOUT)
+		device_printf(sc->rl_dev, "Unable to stop Tx/Rx MAC\n");
+
+	/*
+	 * Free the TX list buffers.
+	 */
+	for (i = 0; i < RL_TX_LIST_CNT; i++) {
+		if (sc->rl_cdata.rl_tx_chain[i] != NULL) {
+			bus_dmamap_sync(sc->rl_cdata.rl_tx_tag,
+			    sc->rl_cdata.rl_tx_dmamap[i],
+			    BUS_DMASYNC_POSTWRITE);
+			bus_dmamap_unload(sc->rl_cdata.rl_tx_tag,
+			    sc->rl_cdata.rl_tx_dmamap[i]);
+			m_freem(sc->rl_cdata.rl_tx_chain[i]);
+			sc->rl_cdata.rl_tx_chain[i] = NULL;
+			CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(uint32_t)),
+			    0x0000000);
+		}
+	}
+}
+
+/*
+ * Device suspend routine.  Stop the interface and save some PCI
+ * settings in case the BIOS doesn't restore them properly on
+ * resume.
+ */
+static int
+rl_suspend(device_t dev)
+{
+	struct rl_softc		*sc;
+
+	sc = device_get_softc(dev);
+
+	RL_LOCK(sc);
+	rl_stop(sc);
+	rl_setwol(sc);
+	sc->suspended = 1;
+	RL_UNLOCK(sc);
+
+	return (0);
+}
+
+/*
+ * Device resume routine.  Restore some PCI settings in case the BIOS
+ * doesn't, re-enable busmastering, and restart the interface if
+ * appropriate.
+ */
+static int
+rl_resume(device_t dev)
+{
+	struct rl_softc		*sc;
+	struct ifnet		*ifp;
+	int			pmc;
+	uint16_t		pmstat;
+
+	sc = device_get_softc(dev);
+	ifp = sc->rl_ifp;
+
+	RL_LOCK(sc);
+
+	if ((ifp->if_capabilities & IFCAP_WOL) != 0 &&
+	    pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
+		/* Disable PME and clear PME status. */
+		pmstat = pci_read_config(sc->rl_dev,
+		    pmc + PCIR_POWER_STATUS, 2);
+		if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) {
+			pmstat &= ~PCIM_PSTAT_PMEENABLE;
+			pci_write_config(sc->rl_dev,
+			    pmc + PCIR_POWER_STATUS, pmstat, 2);
+		}
+		/*
+		 * Clear WOL matching such that normal Rx filtering
+		 * wouldn't interfere with WOL patterns.
+		 */
+		rl_clrwol(sc);
+	}
+
+	/* reinitialize interface if necessary */
+	if (ifp->if_flags & IFF_UP)
+		rl_init_locked(sc);
+
+	sc->suspended = 0;
+
+	RL_UNLOCK(sc);
+
+	return (0);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+rl_shutdown(device_t dev)
+{
+	struct rl_softc		*sc;
+
+	sc = device_get_softc(dev);
+
+	RL_LOCK(sc);
+	rl_stop(sc);
+	/*
+	 * Mark interface as down since otherwise we will panic if
+	 * interrupt comes in later on, which can happen in some
+	 * cases.
+	 */
+	sc->rl_ifp->if_flags &= ~IFF_UP;
+	rl_setwol(sc);
+	RL_UNLOCK(sc);
+
+	return (0);
+}
+
+static void
+rl_setwol(struct rl_softc *sc)
+{
+	struct ifnet		*ifp;
+	int			pmc;
+	uint16_t		pmstat;
+	uint8_t			v;
+
+	RL_LOCK_ASSERT(sc);
+
+	ifp = sc->rl_ifp;
+	if ((ifp->if_capabilities & IFCAP_WOL) == 0)
+		return;
+	if (pci_find_cap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+		return;
+
+	/* Enable config register write. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+	/* Enable PME. */
+	v = CSR_READ_1(sc, sc->rl_cfg1);
+	v &= ~RL_CFG1_PME;
+	if ((ifp->if_capenable & IFCAP_WOL) != 0)
+		v |= RL_CFG1_PME;
+	CSR_WRITE_1(sc, sc->rl_cfg1, v);
+
+	v = CSR_READ_1(sc, sc->rl_cfg3);
+	v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+		v |= RL_CFG3_WOL_MAGIC;
+	CSR_WRITE_1(sc, sc->rl_cfg3, v);
+
+	v = CSR_READ_1(sc, sc->rl_cfg5);
+	v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+	v &= ~RL_CFG5_WOL_LANWAKE;
+	if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0)
+		v |= RL_CFG5_WOL_UCAST;
+	if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
+		v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST;
+	if ((ifp->if_capenable & IFCAP_WOL) != 0)
+		v |= RL_CFG5_WOL_LANWAKE;
+	CSR_WRITE_1(sc, sc->rl_cfg5, v);
+
+	/* Config register write done. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	/* Request PME if WOL is requested. */
+	pmstat = pci_read_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, 2);
+	pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
+	if ((ifp->if_capenable & IFCAP_WOL) != 0)
+		pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
+	pci_write_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2);
+}
+
+static void
+rl_clrwol(struct rl_softc *sc)
+{
+	struct ifnet		*ifp;
+	uint8_t			v;
+
+	ifp = sc->rl_ifp;
+	if ((ifp->if_capabilities & IFCAP_WOL) == 0)
+		return;
+
+	/* Enable config register write. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+	v = CSR_READ_1(sc, sc->rl_cfg3);
+	v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+	CSR_WRITE_1(sc, sc->rl_cfg3, v);
+
+	/* Config register write done. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	v = CSR_READ_1(sc, sc->rl_cfg5);
+	v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+	v &= ~RL_CFG5_WOL_LANWAKE;
+	CSR_WRITE_1(sc, sc->rl_cfg5, v);
+}


Property changes on: trunk/sys/dev/rl/if_rl.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/rl/if_rlreg.h
===================================================================
--- trunk/sys/dev/rl/if_rlreg.h	                        (rev 0)
+++ trunk/sys/dev/rl/if_rlreg.h	2018-05-27 23:14:32 UTC (rev 10071)
@@ -0,0 +1,1161 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 1997, 1998-2003
+ *	Bill Paul <wpaul at ctr.columbia.edu>.  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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/sys/dev/rl/if_rlreg.h 292783 2015-12-27 17:34:18Z marius $
+ */
+
+/*
+ * RealTek 8129/8139 register offsets
+ */
+#define	RL_IDR0		0x0000		/* ID register 0 (station addr) */
+#define	RL_IDR1		0x0001		/* Must use 32-bit accesses (?) */
+#define	RL_IDR2		0x0002
+#define	RL_IDR3		0x0003
+#define	RL_IDR4		0x0004
+#define	RL_IDR5		0x0005
+					/* 0006-0007 reserved */
+#define	RL_MAR0		0x0008		/* Multicast hash table */
+#define	RL_MAR1		0x0009
+#define	RL_MAR2		0x000A
+#define	RL_MAR3		0x000B
+#define	RL_MAR4		0x000C
+#define	RL_MAR5		0x000D
+#define	RL_MAR6		0x000E
+#define	RL_MAR7		0x000F
+
+#define	RL_TXSTAT0	0x0010		/* status of TX descriptor 0 */
+#define	RL_TXSTAT1	0x0014		/* status of TX descriptor 1 */
+#define	RL_TXSTAT2	0x0018		/* status of TX descriptor 2 */
+#define	RL_TXSTAT3	0x001C		/* status of TX descriptor 3 */
+
+#define	RL_TXADDR0	0x0020		/* address of TX descriptor 0 */
+#define	RL_TXADDR1	0x0024		/* address of TX descriptor 1 */
+#define	RL_TXADDR2	0x0028		/* address of TX descriptor 2 */
+#define	RL_TXADDR3	0x002C		/* address of TX descriptor 3 */
+
+#define	RL_RXADDR		0x0030	/* RX ring start address */
+#define	RL_RX_EARLY_BYTES	0x0034	/* RX early byte count */
+#define	RL_RX_EARLY_STAT	0x0036	/* RX early status */
+#define	RL_COMMAND	0x0037		/* command register */
+#define	RL_CURRXADDR	0x0038		/* current address of packet read */
+#define	RL_CURRXBUF	0x003A		/* current RX buffer address */
+#define	RL_IMR		0x003C		/* interrupt mask register */
+#define	RL_ISR		0x003E		/* interrupt status register */
+#define	RL_TXCFG	0x0040		/* transmit config */
+#define	RL_RXCFG	0x0044		/* receive config */
+#define	RL_TIMERCNT	0x0048		/* timer count register */
+#define	RL_MISSEDPKT	0x004C		/* missed packet counter */
+#define	RL_EECMD	0x0050		/* EEPROM command register */
+
+/* RTL8139/RTL8139C+ only */
+#define	RL_8139_CFG0	0x0051		/* config register #0 */
+#define	RL_8139_CFG1	0x0052		/* config register #1 */
+#define	RL_8139_CFG3	0x0059		/* config register #3 */
+#define	RL_8139_CFG4	0x005A		/* config register #4 */
+#define	RL_8139_CFG5	0x00D8		/* config register #5 */
+
+#define	RL_CFG0		0x0051		/* config register #0 */
+#define	RL_CFG1		0x0052		/* config register #1 */
+#define	RL_CFG2		0x0053		/* config register #2 */
+#define	RL_CFG3		0x0054		/* config register #3 */
+#define	RL_CFG4		0x0055		/* config register #4 */
+#define	RL_CFG5		0x0056		/* config register #5 */
+					/* 0057 reserved */
+#define	RL_MEDIASTAT	0x0058		/* media status register (8139) */
+					/* 0059-005A reserved */
+#define	RL_MII		0x005A		/* 8129 chip only */
+#define	RL_HALTCLK	0x005B
+#define	RL_MULTIINTR	0x005C		/* multiple interrupt */
+#define	RL_PCIREV	0x005E		/* PCI revision value */
+					/* 005F reserved */
+#define	RL_TXSTAT_ALL	0x0060		/* TX status of all descriptors */
+
+/* Direct PHY access registers only available on 8139 */
+#define	RL_BMCR		0x0062		/* PHY basic mode control */
+#define	RL_BMSR		0x0064		/* PHY basic mode status */
+#define	RL_ANAR		0x0066		/* PHY autoneg advert */
+#define	RL_LPAR		0x0068		/* PHY link partner ability */
+#define	RL_ANER		0x006A		/* PHY autoneg expansion */
+
+#define	RL_DISCCNT	0x006C		/* disconnect counter */
+#define	RL_FALSECAR	0x006E		/* false carrier counter */
+#define	RL_NWAYTST	0x0070		/* NWAY test register */
+#define	RL_RX_ER	0x0072		/* RX_ER counter */
+#define	RL_CSCFG	0x0074		/* CS configuration register */
+
+/*
+ * When operating in special C+ mode, some of the registers in an
+ * 8139C+ chip have different definitions. These are also used for
+ * the 8169 gigE chip.
+ */
+#define	RL_DUMPSTATS_LO		0x0010	/* counter dump command register */
+#define	RL_DUMPSTATS_HI		0x0014	/* counter dump command register */
+#define	RL_TXLIST_ADDR_LO	0x0020	/* 64 bits, 256 byte alignment */
+#define	RL_TXLIST_ADDR_HI	0x0024	/* 64 bits, 256 byte alignment */
+#define	RL_TXLIST_ADDR_HPRIO_LO	0x0028	/* 64 bits, 256 byte alignment */
+#define	RL_TXLIST_ADDR_HPRIO_HI	0x002C	/* 64 bits, 256 byte alignment */
+#define	RL_CFG2			0x0053
+#define	RL_TIMERINT		0x0054	/* interrupt on timer expire */
+#define	RL_TXSTART		0x00D9	/* 8 bits */
+#define	RL_CPLUS_CMD		0x00E0	/* 16 bits */
+#define	RL_RXLIST_ADDR_LO	0x00E4	/* 64 bits, 256 byte alignment */
+#define	RL_RXLIST_ADDR_HI	0x00E8	/* 64 bits, 256 byte alignment */
+#define	RL_EARLY_TX_THRESH	0x00EC	/* 8 bits */
+
+/*
+ * Registers specific to the 8169 gigE chip
+ */
+#define	RL_GTXSTART		0x0038	/* 8 bits */
+#define	RL_TIMERINT_8169	0x0058	/* different offset than 8139 */
+#define	RL_PHYAR		0x0060
+#define	RL_TBICSR		0x0064
+#define	RL_TBI_ANAR		0x0068
+#define	RL_TBI_LPAR		0x006A
+#define	RL_GMEDIASTAT		0x006C	/* 8 bits */
+#define	RL_MACDBG		0x006D	/* 8 bits, 8168C SPIN2 only */
+#define	RL_GPIO			0x006E	/* 8 bits, 8168C SPIN2 only */
+#define	RL_PMCH			0x006F	/* 8 bits */
+#define	RL_MAXRXPKTLEN		0x00DA	/* 16 bits, chip multiplies by 8 */
+#define	RL_INTRMOD		0x00E2	/* 16 bits */
+#define	RL_MISC			0x00F0
+
+/*
+ * TX config register bits
+ */
+#define	RL_TXCFG_CLRABRT	0x00000001	/* retransmit aborted pkt */
+#define	RL_TXCFG_MAXDMA		0x00000700	/* max DMA burst size */
+#define	RL_TXCFG_QUEUE_EMPTY	0x00000800	/* 8168E-VL or higher */
+#define	RL_TXCFG_CRCAPPEND	0x00010000	/* CRC append (0 = yes) */
+#define	RL_TXCFG_LOOPBKTST	0x00060000	/* loopback test */
+#define	RL_TXCFG_IFG2		0x00080000	/* 8169 only */
+#define	RL_TXCFG_IFG		0x03000000	/* interframe gap */
+#define	RL_TXCFG_HWREV		0x7CC00000
+
+#define	RL_LOOPTEST_OFF		0x00000000
+#define	RL_LOOPTEST_ON		0x00020000
+#define	RL_LOOPTEST_ON_CPLUS	0x00060000
+
+/* Known revision codes. */
+#define	RL_HWREV_8169		0x00000000
+#define	RL_HWREV_8169S		0x00800000
+#define	RL_HWREV_8110S		0x04000000
+#define	RL_HWREV_8169_8110SB	0x10000000
+#define	RL_HWREV_8169_8110SC	0x18000000
+#define	RL_HWREV_8401E		0x24000000
+#define	RL_HWREV_8102EL		0x24800000
+#define	RL_HWREV_8102EL_SPIN1	0x24C00000
+#define	RL_HWREV_8168D		0x28000000
+#define	RL_HWREV_8168DP		0x28800000
+#define	RL_HWREV_8168E		0x2C000000
+#define	RL_HWREV_8168E_VL	0x2C800000
+#define	RL_HWREV_8168B_SPIN1	0x30000000
+#define	RL_HWREV_8100E		0x30800000
+#define	RL_HWREV_8101E		0x34000000
+#define	RL_HWREV_8102E		0x34800000
+#define	RL_HWREV_8103E		0x34C00000
+#define	RL_HWREV_8168B_SPIN2	0x38000000
+#define	RL_HWREV_8168B_SPIN3	0x38400000
+#define	RL_HWREV_8168C		0x3C000000
+#define	RL_HWREV_8168C_SPIN2	0x3C400000
+#define	RL_HWREV_8168CP		0x3C800000
+#define	RL_HWREV_8105E		0x40800000
+#define	RL_HWREV_8105E_SPIN1	0x40C00000
+#define	RL_HWREV_8402		0x44000000
+#define	RL_HWREV_8106E		0x44800000
+#define	RL_HWREV_8168F		0x48000000
+#define	RL_HWREV_8411		0x48800000
+#define	RL_HWREV_8168G		0x4C000000
+#define	RL_HWREV_8168EP		0x50000000
+#define	RL_HWREV_8168GU		0x50800000
+#define	RL_HWREV_8168H		0x54000000
+#define	RL_HWREV_8411B		0x5C800000
+#define	RL_HWREV_8139		0x60000000
+#define	RL_HWREV_8139A		0x70000000
+#define	RL_HWREV_8139AG		0x70800000
+#define	RL_HWREV_8139B		0x78000000
+#define	RL_HWREV_8130		0x7C000000
+#define	RL_HWREV_8139C		0x74000000
+#define	RL_HWREV_8139D		0x74400000
+#define	RL_HWREV_8139CPLUS	0x74800000
+#define	RL_HWREV_8101		0x74C00000
+#define	RL_HWREV_8100		0x78800000
+#define	RL_HWREV_8169_8110SBL	0x7CC00000
+#define	RL_HWREV_8169_8110SCE	0x98000000
+
+#define	RL_TXDMA_16BYTES	0x00000000
+#define	RL_TXDMA_32BYTES	0x00000100
+#define	RL_TXDMA_64BYTES	0x00000200
+#define	RL_TXDMA_128BYTES	0x00000300
+#define	RL_TXDMA_256BYTES	0x00000400
+#define	RL_TXDMA_512BYTES	0x00000500
+#define	RL_TXDMA_1024BYTES	0x00000600
+#define	RL_TXDMA_2048BYTES	0x00000700
+
+/*
+ * Transmit descriptor status register bits.
+ */
+#define	RL_TXSTAT_LENMASK	0x00001FFF
+#define	RL_TXSTAT_OWN		0x00002000
+#define	RL_TXSTAT_TX_UNDERRUN	0x00004000
+#define	RL_TXSTAT_TX_OK		0x00008000
+#define	RL_TXSTAT_EARLY_THRESH	0x003F0000
+#define	RL_TXSTAT_COLLCNT	0x0F000000
+#define	RL_TXSTAT_CARR_HBEAT	0x10000000
+#define	RL_TXSTAT_OUTOFWIN	0x20000000
+#define	RL_TXSTAT_TXABRT	0x40000000
+#define	RL_TXSTAT_CARRLOSS	0x80000000
+
+/*
+ * Interrupt status register bits.
+ */
+#define	RL_ISR_RX_OK		0x0001
+#define	RL_ISR_RX_ERR		0x0002
+#define	RL_ISR_TX_OK		0x0004
+#define	RL_ISR_TX_ERR		0x0008
+#define	RL_ISR_RX_OVERRUN	0x0010
+#define	RL_ISR_PKT_UNDERRUN	0x0020
+#define	RL_ISR_LINKCHG		0x0020	/* 8169 only */
+#define	RL_ISR_FIFO_OFLOW	0x0040	/* 8139 only */
+#define	RL_ISR_TX_DESC_UNAVAIL	0x0080	/* C+ only */
+#define	RL_ISR_SWI		0x0100	/* C+ only */
+#define	RL_ISR_CABLE_LEN_CHGD	0x2000
+#define	RL_ISR_PCS_TIMEOUT	0x4000	/* 8129 only */
+#define	RL_ISR_TIMEOUT_EXPIRED	0x4000
+#define	RL_ISR_SYSTEM_ERR	0x8000
+
+#define	RL_INTRS	\
+	(RL_ISR_TX_OK|RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|		\
+	RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW|	\
+	RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR)
+
+#ifdef RE_TX_MODERATION
+#define	RL_INTRS_CPLUS	\
+	(RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|			\
+	RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW|	\
+	RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED)
+#else
+#define	RL_INTRS_CPLUS	\
+	(RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|RL_ISR_TX_OK|		\
+	RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW|	\
+	RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED)
+#endif
+
+/*
+ * Media status register. (8139 only)
+ */
+#define	RL_MEDIASTAT_RXPAUSE	0x01
+#define	RL_MEDIASTAT_TXPAUSE	0x02
+#define	RL_MEDIASTAT_LINK	0x04
+#define	RL_MEDIASTAT_SPEED10	0x08
+#define	RL_MEDIASTAT_RXFLOWCTL	0x40	/* duplex mode */
+#define	RL_MEDIASTAT_TXFLOWCTL	0x80	/* duplex mode */
+
+/*
+ * Receive config register.
+ */
+#define	RL_RXCFG_RX_ALLPHYS	0x00000001	/* accept all nodes */
+#define	RL_RXCFG_RX_INDIV	0x00000002	/* match filter */
+#define	RL_RXCFG_RX_MULTI	0x00000004	/* accept all multicast */
+#define	RL_RXCFG_RX_BROAD	0x00000008	/* accept all broadcast */
+#define	RL_RXCFG_RX_RUNT	0x00000010
+#define	RL_RXCFG_RX_ERRPKT	0x00000020
+#define	RL_RXCFG_WRAP		0x00000080
+#define	RL_RXCFG_EARLYOFFV2	0x00000800
+#define	RL_RXCFG_MAXDMA		0x00000700
+#define	RL_RXCFG_BUFSZ		0x00001800
+#define	RL_RXCFG_EARLYOFF	0x00003800
+#define	RL_RXCFG_FIFOTHRESH	0x0000E000
+#define	RL_RXCFG_EARLYTHRESH	0x07000000
+
+#define	RL_RXDMA_16BYTES	0x00000000
+#define	RL_RXDMA_32BYTES	0x00000100
+#define	RL_RXDMA_64BYTES	0x00000200
+#define	RL_RXDMA_128BYTES	0x00000300
+#define	RL_RXDMA_256BYTES	0x00000400
+#define	RL_RXDMA_512BYTES	0x00000500
+#define	RL_RXDMA_1024BYTES	0x00000600
+#define	RL_RXDMA_UNLIMITED	0x00000700
+
+#define	RL_RXBUF_8		0x00000000
+#define	RL_RXBUF_16		0x00000800
+#define	RL_RXBUF_32		0x00001000
+#define	RL_RXBUF_64		0x00001800
+
+#define	RL_RXFIFO_16BYTES	0x00000000
+#define	RL_RXFIFO_32BYTES	0x00002000
+#define	RL_RXFIFO_64BYTES	0x00004000
+#define	RL_RXFIFO_128BYTES	0x00006000
+#define	RL_RXFIFO_256BYTES	0x00008000
+#define	RL_RXFIFO_512BYTES	0x0000A000
+#define	RL_RXFIFO_1024BYTES	0x0000C000
+#define	RL_RXFIFO_NOTHRESH	0x0000E000
+
+/*
+ * Bits in RX status header (included with RX'ed packet
+ * in ring buffer).
+ */
+#define	RL_RXSTAT_RXOK		0x00000001
+#define	RL_RXSTAT_ALIGNERR	0x00000002
+#define	RL_RXSTAT_CRCERR	0x00000004
+#define	RL_RXSTAT_GIANT		0x00000008
+#define	RL_RXSTAT_RUNT		0x00000010
+#define	RL_RXSTAT_BADSYM	0x00000020
+#define	RL_RXSTAT_BROAD		0x00002000
+#define	RL_RXSTAT_INDIV		0x00004000
+#define	RL_RXSTAT_MULTI		0x00008000
+#define	RL_RXSTAT_LENMASK	0xFFFF0000
+#define	RL_RXSTAT_UNFINISHED	0x0000FFF0	/* DMA still in progress */
+
+/*
+ * Command register.
+ */
+#define	RL_CMD_EMPTY_RXBUF	0x0001
+#define	RL_CMD_TX_ENB		0x0004
+#define	RL_CMD_RX_ENB		0x0008
+#define	RL_CMD_RESET		0x0010
+#define	RL_CMD_STOPREQ		0x0080
+
+/*
+ * Twister register values.  These are completely undocumented and derived
+ * from public sources.
+ */
+#define	RL_CSCFG_LINK_OK	0x0400
+#define	RL_CSCFG_CHANGE		0x0800
+#define	RL_CSCFG_STATUS		0xf000
+#define	RL_CSCFG_ROW3		0x7000
+#define	RL_CSCFG_ROW2		0x3000
+#define	RL_CSCFG_ROW1		0x1000
+#define	RL_CSCFG_LINK_DOWN_OFF_CMD 0x03c0
+#define	RL_CSCFG_LINK_DOWN_CMD	0xf3c0
+
+#define	RL_NWAYTST_RESET	0
+#define	RL_NWAYTST_CBL_TEST	0x20
+
+#define	RL_PARA78		0x78
+#define	RL_PARA78_DEF		0x78fa8388
+#define	RL_PARA7C		0x7C
+#define	RL_PARA7C_DEF		0xcb38de43
+#define	RL_PARA7C_RETUNE	0xfb38de03
+
+/*
+ * EEPROM control register
+ */
+#define	RL_EE_DATAOUT		0x01	/* Data out */
+#define	RL_EE_DATAIN		0x02	/* Data in */
+#define	RL_EE_CLK		0x04	/* clock */
+#define	RL_EE_SEL		0x08	/* chip select */
+#define	RL_EE_MODE		(0x40|0x80)
+
+#define	RL_EEMODE_OFF		0x00
+#define	RL_EEMODE_AUTOLOAD	0x40
+#define	RL_EEMODE_PROGRAM	0x80
+#define	RL_EEMODE_WRITECFG	(0x80|0x40)
+
+/* 9346 EEPROM commands */
+#define	RL_9346_ADDR_LEN	6	/* 93C46 1K: 128x16 */
+#define	RL_9356_ADDR_LEN	8	/* 93C56 2K: 256x16 */
+
+#define	RL_9346_WRITE		0x5
+#define	RL_9346_READ		0x6
+#define	RL_9346_ERASE		0x7
+#define	RL_9346_EWEN		0x4
+#define	RL_9346_EWEN_ADDR	0x30
+#define	RL_9456_EWDS		0x4
+#define	RL_9346_EWDS_ADDR	0x00
+
+#define	RL_EECMD_WRITE		0x140
+#define	RL_EECMD_READ_6BIT	0x180
+#define	RL_EECMD_READ_8BIT	0x600
+#define	RL_EECMD_ERASE		0x1c0
+
+#define	RL_EE_ID		0x00
+#define	RL_EE_PCI_VID		0x01
+#define	RL_EE_PCI_DID		0x02
+/* Location of station address inside EEPROM */
+#define	RL_EE_EADDR		0x07
+
+/*
+ * MII register (8129 only)
+ */
+#define	RL_MII_CLK		0x01
+#define	RL_MII_DATAIN		0x02
+#define	RL_MII_DATAOUT		0x04
+#define	RL_MII_DIR		0x80	/* 0 == input, 1 == output */
+
+/*
+ * Config 0 register
+ */
+#define	RL_CFG0_ROM0		0x01
+#define	RL_CFG0_ROM1		0x02
+#define	RL_CFG0_ROM2		0x04
+#define	RL_CFG0_PL0		0x08
+#define	RL_CFG0_PL1		0x10
+#define	RL_CFG0_10MBPS		0x20	/* 10 Mbps internal mode */
+#define	RL_CFG0_PCS		0x40
+#define	RL_CFG0_SCR		0x80
+
+/*
+ * Config 1 register
+ */
+#define	RL_CFG1_PWRDWN		0x01
+#define	RL_CFG1_PME		0x01
+#define	RL_CFG1_SLEEP		0x02
+#define	RL_CFG1_VPDEN		0x02
+#define	RL_CFG1_IOMAP		0x04
+#define	RL_CFG1_MEMMAP		0x08
+#define	RL_CFG1_RSVD		0x10
+#define	RL_CFG1_LWACT		0x10
+#define	RL_CFG1_DRVLOAD		0x20
+#define	RL_CFG1_LED0		0x40
+#define	RL_CFG1_FULLDUPLEX	0x40	/* 8129 only */
+#define	RL_CFG1_LED1		0x80
+
+/*
+ * Config 2 register
+ */
+#define	RL_CFG2_PCI33MHZ	0x00
+#define	RL_CFG2_PCI66MHZ	0x01
+#define	RL_CFG2_PCI64BIT	0x08
+#define	RL_CFG2_AUXPWR		0x10
+#define	RL_CFG2_MSI		0x20
+
+/*
+ * Config 3 register
+ */
+#define	RL_CFG3_GRANTSEL	0x80
+#define	RL_CFG3_WOL_MAGIC	0x20
+#define	RL_CFG3_WOL_LINK	0x10
+#define	RL_CFG3_JUMBO_EN0	0x04	/* RTL8168C or later. */
+#define	RL_CFG3_FAST_B2B	0x01
+
+/*
+ * Config 4 register
+ */
+#define	RL_CFG4_LWPTN		0x04
+#define	RL_CFG4_LWPME		0x10
+#define	RL_CFG4_JUMBO_EN1	0x02	/* RTL8168C or later. */
+
+/*
+ * Config 5 register
+ */
+#define	RL_CFG5_WOL_BCAST	0x40
+#define	RL_CFG5_WOL_MCAST	0x20
+#define	RL_CFG5_WOL_UCAST	0x10
+#define	RL_CFG5_WOL_LANWAKE	0x02
+#define	RL_CFG5_PME_STS		0x01
+
+/*
+ * 8139C+ register definitions
+ */
+
+/* RL_DUMPSTATS_LO register */
+#define	RL_DUMPSTATS_START	0x00000008
+
+/* Transmit start register */
+#define	RL_TXSTART_SWI		0x01	/* generate TX interrupt */
+#define	RL_TXSTART_START	0x40	/* start normal queue transmit */
+#define	RL_TXSTART_HPRIO_START	0x80	/* start hi prio queue transmit */
+
+/*
+ * Config 2 register, 8139C+/8169/8169S/8110S only
+ */
+#define	RL_CFG2_BUSFREQ		0x07
+#define	RL_CFG2_BUSWIDTH	0x08
+#define	RL_CFG2_AUXPWRSTS	0x10
+
+#define	RL_BUSFREQ_33MHZ	0x00
+#define	RL_BUSFREQ_66MHZ	0x01
+
+#define	RL_BUSWIDTH_32BITS	0x00
+#define	RL_BUSWIDTH_64BITS	0x08
+
+/* C+ mode command register */
+#define	RL_CPLUSCMD_TXENB	0x0001	/* enable C+ transmit mode */
+#define	RL_CPLUSCMD_RXENB	0x0002	/* enable C+ receive mode */
+#define	RL_CPLUSCMD_PCI_MRW	0x0008	/* enable PCI multi-read/write */
+#define	RL_CPLUSCMD_PCI_DAC	0x0010	/* PCI dual-address cycle only */
+#define	RL_CPLUSCMD_RXCSUM_ENB	0x0020	/* enable RX checksum offload */
+#define	RL_CPLUSCMD_VLANSTRIP	0x0040	/* enable VLAN tag stripping */
+#define	RL_CPLUSCMD_MACSTAT_DIS	0x0080	/* 8168B/C/CP */
+#define	RL_CPLUSCMD_ASF		0x0100	/* 8168C/CP */
+#define	RL_CPLUSCMD_DBG_SEL	0x0200	/* 8168C/CP */
+#define	RL_CPLUSCMD_FORCE_TXFC	0x0400	/* 8168C/CP */
+#define	RL_CPLUSCMD_FORCE_RXFC	0x0800	/* 8168C/CP */
+#define	RL_CPLUSCMD_FORCE_HDPX	0x1000	/* 8168C/CP */
+#define	RL_CPLUSCMD_NORMAL_MODE	0x2000	/* 8168C/CP */
+#define	RL_CPLUSCMD_DBG_ENB	0x4000	/* 8168C/CP */
+#define	RL_CPLUSCMD_BIST_ENB	0x8000	/* 8168C/CP */
+
+/* C+ early transmit threshold */
+#define	RL_EARLYTXTHRESH_CNT	0x003F	/* byte count times 8 */
+
+/* Timer interrupt register */
+#define	RL_TIMERINT_8169_VAL	0x00001FFF
+#define	RL_TIMER_MIN		0
+#define	RL_TIMER_MAX		65	/* 65.528us */
+#define	RL_TIMER_DEFAULT	RL_TIMER_MAX
+#define	RL_TIMER_PCIE_CLK	125	/* 125MHZ */
+#define	RL_USECS(x)		((x) * RL_TIMER_PCIE_CLK)
+
+/*
+ * Gigabit PHY access register (8169 only)
+ */
+#define	RL_PHYAR_PHYDATA	0x0000FFFF
+#define	RL_PHYAR_PHYREG		0x001F0000
+#define	RL_PHYAR_BUSY		0x80000000
+
+/*
+ * Gigabit media status (8169 only)
+ */
+#define	RL_GMEDIASTAT_FDX	0x01	/* full duplex */
+#define	RL_GMEDIASTAT_LINK	0x02	/* link up */
+#define	RL_GMEDIASTAT_10MBPS	0x04	/* 10mps link */
+#define	RL_GMEDIASTAT_100MBPS	0x08	/* 100mbps link */
+#define	RL_GMEDIASTAT_1000MBPS	0x10	/* gigE link */
+#define	RL_GMEDIASTAT_RXFLOW	0x20	/* RX flow control on */
+#define	RL_GMEDIASTAT_TXFLOW	0x40	/* TX flow control on */
+#define	RL_GMEDIASTAT_TBI	0x80	/* TBI enabled */
+
+/*
+ * The RealTek doesn't use a fragment-based descriptor mechanism.
+ * Instead, there are only four register sets, each or which represents
+ * one 'descriptor.' Basically, each TX descriptor is just a contiguous
+ * packet buffer (32-bit aligned!) and we place the buffer addresses in
+ * the registers so the chip knows where they are.
+ *
+ * We can sort of kludge together the same kind of buffer management
+ * used in previous drivers, but we have to do buffer copies almost all
+ * the time, so it doesn't really buy us much.
+ *
+ * For reception, there's just one large buffer where the chip stores
+ * all received packets.
+ */
+#define	RL_RX_BUF_SZ		RL_RXBUF_64
+#define	RL_RXBUFLEN		(1 << ((RL_RX_BUF_SZ >> 11) + 13))
+#define	RL_TX_LIST_CNT		4
+#define	RL_MIN_FRAMELEN		60
+#define	RL_TX_8139_BUF_ALIGN	4
+#define	RL_RX_8139_BUF_ALIGN	8
+#define	RL_RX_8139_BUF_RESERVE	sizeof(int64_t)
+#define	RL_RX_8139_BUF_GUARD_SZ	\
+	(ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN + RL_RX_8139_BUF_RESERVE)
+#define	RL_TXTHRESH(x)		((x) << 11)
+#define	RL_TX_THRESH_INIT	96
+#define	RL_RX_FIFOTHRESH	RL_RXFIFO_NOTHRESH
+#define	RL_RX_MAXDMA		RL_RXDMA_UNLIMITED
+#define	RL_TX_MAXDMA		RL_TXDMA_2048BYTES
+
+#define	RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ)
+#define	RL_TXCFG_CONFIG	(RL_TXCFG_IFG|RL_TX_MAXDMA)
+
+#define	RL_ETHER_ALIGN	2
+
+/*
+ * re(4) hardware ip4csum-tx could be mangled with 28 bytes or less IP packets.
+ */
+#define	RL_IP4CSUMTX_MINLEN	28
+#define	RL_IP4CSUMTX_PADLEN	(ETHER_HDR_LEN + RL_IP4CSUMTX_MINLEN)
+
+struct rl_chain_data {
+	uint16_t		cur_rx;
+	uint8_t			*rl_rx_buf;
+	uint8_t			*rl_rx_buf_ptr;
+
+	struct mbuf		*rl_tx_chain[RL_TX_LIST_CNT];
+	bus_dmamap_t		rl_tx_dmamap[RL_TX_LIST_CNT];
+	bus_dma_tag_t		rl_tx_tag;
+	bus_dma_tag_t		rl_rx_tag;
+	bus_dmamap_t		rl_rx_dmamap;
+	bus_addr_t		rl_rx_buf_paddr;
+	uint8_t			last_tx;
+	uint8_t			cur_tx;
+};
+
+#define	RL_INC(x)		(x = (x + 1) % RL_TX_LIST_CNT)
+#define	RL_CUR_TXADDR(x)	((x->rl_cdata.cur_tx * 4) + RL_TXADDR0)
+#define	RL_CUR_TXSTAT(x)	((x->rl_cdata.cur_tx * 4) + RL_TXSTAT0)
+#define	RL_CUR_TXMBUF(x)	(x->rl_cdata.rl_tx_chain[x->rl_cdata.cur_tx])
+#define	RL_CUR_DMAMAP(x)	(x->rl_cdata.rl_tx_dmamap[x->rl_cdata.cur_tx])
+#define	RL_LAST_TXADDR(x)	((x->rl_cdata.last_tx * 4) + RL_TXADDR0)
+#define	RL_LAST_TXSTAT(x)	((x->rl_cdata.last_tx * 4) + RL_TXSTAT0)
+#define	RL_LAST_TXMBUF(x)	(x->rl_cdata.rl_tx_chain[x->rl_cdata.last_tx])
+#define	RL_LAST_DMAMAP(x)	(x->rl_cdata.rl_tx_dmamap[x->rl_cdata.last_tx])
+
+struct rl_type {
+	uint16_t		rl_vid;
+	uint16_t		rl_did;
+	int			rl_basetype;
+	const char		*rl_name;
+};
+
+struct rl_hwrev {
+	uint32_t		rl_rev;
+	int			rl_type;
+	const char		*rl_desc;
+	int			rl_max_mtu;
+};
+
+#define	RL_8129			1
+#define	RL_8139			2
+#define	RL_8139CPLUS		3
+#define	RL_8169			4
+
+#define	RL_ISCPLUS(x)		((x)->rl_type == RL_8139CPLUS ||	\
+				 (x)->rl_type == RL_8169)
+
+/*
+ * The 8139C+ and 8160 gigE chips support descriptor-based TX
+ * and RX. In fact, they even support TCP large send. Descriptors
+ * must be allocated in contiguous blocks that are aligned on a
+ * 256-byte boundary. The rings can hold a maximum of 64 descriptors.
+ */
+
+/*
+ * RX/TX descriptor definition. When large send mode is enabled, the
+ * lower 11 bits of the TX rl_cmdstat word are used to hold the MSS, and
+ * the checksum offload bits are disabled. The structure layout is
+ * the same for RX and TX descriptors
+ */
+struct rl_desc {
+	uint32_t		rl_cmdstat;
+	uint32_t		rl_vlanctl;
+	uint32_t		rl_bufaddr_lo;
+	uint32_t		rl_bufaddr_hi;
+};
+
+#define	RL_TDESC_CMD_FRAGLEN	0x0000FFFF
+#define	RL_TDESC_CMD_TCPCSUM	0x00010000	/* TCP checksum enable */
+#define	RL_TDESC_CMD_UDPCSUM	0x00020000	/* UDP checksum enable */
+#define	RL_TDESC_CMD_IPCSUM	0x00040000	/* IP header checksum enable */
+#define	RL_TDESC_CMD_MSSVAL	0x07FF0000	/* Large send MSS value */
+#define	RL_TDESC_CMD_MSSVAL_SHIFT	16	/* Large send MSS value shift */
+#define	RL_TDESC_CMD_LGSEND	0x08000000	/* TCP large send enb */
+#define	RL_TDESC_CMD_EOF	0x10000000	/* end of frame marker */
+#define	RL_TDESC_CMD_SOF	0x20000000	/* start of frame marker */
+#define	RL_TDESC_CMD_EOR	0x40000000	/* end of ring marker */
+#define	RL_TDESC_CMD_OWN	0x80000000	/* chip owns descriptor */
+
+#define	RL_TDESC_VLANCTL_TAG	0x00020000	/* Insert VLAN tag */
+#define	RL_TDESC_VLANCTL_DATA	0x0000FFFF	/* TAG data */
+/* RTL8168C/RTL8168CP/RTL8111C/RTL8111CP */
+#define	RL_TDESC_CMD_UDPCSUMV2	0x80000000
+#define	RL_TDESC_CMD_TCPCSUMV2	0x40000000
+#define	RL_TDESC_CMD_IPCSUMV2	0x20000000
+#define	RL_TDESC_CMD_MSSVALV2	0x1FFC0000
+#define	RL_TDESC_CMD_MSSVALV2_SHIFT	18
+
+/*
+ * Error bits are valid only on the last descriptor of a frame
+ * (i.e. RL_TDESC_CMD_EOF == 1)
+ */
+#define	RL_TDESC_STAT_COLCNT	0x000F0000	/* collision count */
+#define	RL_TDESC_STAT_EXCESSCOL	0x00100000	/* excessive collisions */
+#define	RL_TDESC_STAT_LINKFAIL	0x00200000	/* link faulure */
+#define	RL_TDESC_STAT_OWINCOL	0x00400000	/* out-of-window collision */
+#define	RL_TDESC_STAT_TXERRSUM	0x00800000	/* transmit error summary */
+#define	RL_TDESC_STAT_UNDERRUN	0x02000000	/* TX underrun occured */
+#define	RL_TDESC_STAT_OWN	0x80000000
+
+/*
+ * RX descriptor cmd/vlan definitions
+ */
+#define	RL_RDESC_CMD_EOR	0x40000000
+#define	RL_RDESC_CMD_OWN	0x80000000
+#define	RL_RDESC_CMD_BUFLEN	0x00001FFF
+
+#define	RL_RDESC_STAT_OWN	0x80000000
+#define	RL_RDESC_STAT_EOR	0x40000000
+#define	RL_RDESC_STAT_SOF	0x20000000
+#define	RL_RDESC_STAT_EOF	0x10000000
+#define	RL_RDESC_STAT_FRALIGN	0x08000000	/* frame alignment error */
+#define	RL_RDESC_STAT_MCAST	0x04000000	/* multicast pkt received */
+#define	RL_RDESC_STAT_UCAST	0x02000000	/* unicast pkt received */
+#define	RL_RDESC_STAT_BCAST	0x01000000	/* broadcast pkt received */
+#define	RL_RDESC_STAT_BUFOFLOW	0x00800000	/* out of buffer space */
+#define	RL_RDESC_STAT_FIFOOFLOW	0x00400000	/* FIFO overrun */
+#define	RL_RDESC_STAT_GIANT	0x00200000	/* pkt > 4096 bytes */
+#define	RL_RDESC_STAT_RXERRSUM	0x00100000	/* RX error summary */
+#define	RL_RDESC_STAT_RUNT	0x00080000	/* runt packet received */
+#define	RL_RDESC_STAT_CRCERR	0x00040000	/* CRC error */
+#define	RL_RDESC_STAT_PROTOID	0x00030000	/* Protocol type */
+#define	RL_RDESC_STAT_UDP	0x00020000	/* UDP, 8168C/CP, 8111C/CP */
+#define	RL_RDESC_STAT_TCP	0x00010000	/* TCP, 8168C/CP, 8111C/CP */
+#define	RL_RDESC_STAT_IPSUMBAD	0x00008000	/* IP header checksum bad */
+#define	RL_RDESC_STAT_UDPSUMBAD	0x00004000	/* UDP checksum bad */
+#define	RL_RDESC_STAT_TCPSUMBAD	0x00002000	/* TCP checksum bad */
+#define	RL_RDESC_STAT_FRAGLEN	0x00001FFF	/* RX'ed frame/frag len */
+#define	RL_RDESC_STAT_GFRAGLEN	0x00003FFF	/* RX'ed frame/frag len */
+#define	RL_RDESC_STAT_ERRS	(RL_RDESC_STAT_GIANT|RL_RDESC_STAT_RUNT| \
+				 RL_RDESC_STAT_CRCERR)
+
+#define	RL_RDESC_VLANCTL_TAG	0x00010000	/* VLAN tag available
+						   (rl_vlandata valid)*/
+#define	RL_RDESC_VLANCTL_DATA	0x0000FFFF	/* TAG data */
+/* RTL8168C/RTL8168CP/RTL8111C/RTL8111CP */
+#define	RL_RDESC_IPV6		0x80000000
+#define	RL_RDESC_IPV4		0x40000000
+
+#define	RL_PROTOID_NONIP	0x00000000
+#define	RL_PROTOID_TCPIP	0x00010000
+#define	RL_PROTOID_UDPIP	0x00020000
+#define	RL_PROTOID_IP		0x00030000
+#define	RL_TCPPKT(x)		(((x) & RL_RDESC_STAT_PROTOID) == \
+				 RL_PROTOID_TCPIP)
+#define	RL_UDPPKT(x)		(((x) & RL_RDESC_STAT_PROTOID) == \
+				 RL_PROTOID_UDPIP)
+
+/*
+ * Statistics counter structure (8139C+ and 8169 only)
+ */
+struct rl_stats {
+	uint64_t		rl_tx_pkts;
+	uint64_t		rl_rx_pkts;
+	uint64_t		rl_tx_errs;
+	uint32_t		rl_rx_errs;
+	uint16_t		rl_missed_pkts;
+	uint16_t		rl_rx_framealign_errs;
+	uint32_t		rl_tx_onecoll;
+	uint32_t		rl_tx_multicolls;
+	uint64_t		rl_rx_ucasts;
+	uint64_t		rl_rx_bcasts;
+	uint32_t		rl_rx_mcasts;
+	uint16_t		rl_tx_aborts;
+	uint16_t		rl_rx_underruns;
+};
+
+/*
+ * Rx/Tx descriptor parameters (8139C+ and 8169 only)
+ *
+ * 8139C+
+ *  Number of descriptors supported : up to 64
+ *  Descriptor alignment : 256 bytes
+ *  Tx buffer : At least 4 bytes in length.
+ *  Rx buffer : At least 8 bytes in length and 8 bytes alignment required.
+ *
+ * 8169
+ *  Number of descriptors supported : up to 1024
+ *  Descriptor alignment : 256 bytes
+ *  Tx buffer : At least 4 bytes in length.
+ *  Rx buffer : At least 8 bytes in length and 8 bytes alignment required.
+ */
+#ifndef	__NO_STRICT_ALIGNMENT
+#define	RE_FIXUP_RX	1
+#endif
+
+#define	RL_8169_TX_DESC_CNT	256
+#define	RL_8169_RX_DESC_CNT	256
+#define	RL_8139_TX_DESC_CNT	64
+#define	RL_8139_RX_DESC_CNT	64
+#define	RL_TX_DESC_CNT		RL_8169_TX_DESC_CNT
+#define	RL_RX_DESC_CNT		RL_8169_RX_DESC_CNT
+#define	RL_RX_JUMBO_DESC_CNT	RL_RX_DESC_CNT
+#define	RL_NTXSEGS		35
+
+#define	RL_RING_ALIGN		256
+#define	RL_DUMP_ALIGN		64
+#define	RL_IFQ_MAXLEN		512
+#define	RL_TX_DESC_NXT(sc,x)	((x + 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1))
+#define	RL_TX_DESC_PRV(sc,x)	((x - 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1))
+#define	RL_RX_DESC_NXT(sc,x)	((x + 1) & ((sc)->rl_ldata.rl_rx_desc_cnt - 1))
+#define	RL_OWN(x)		(le32toh((x)->rl_cmdstat) & RL_RDESC_STAT_OWN)
+#define	RL_RXBYTES(x)		(le32toh((x)->rl_cmdstat) & sc->rl_rxlenmask)
+#define	RL_PKTSZ(x)		((x)/* >> 3*/)
+#ifdef RE_FIXUP_RX
+#define	RE_ETHER_ALIGN	sizeof(uint64_t)
+#define	RE_RX_DESC_BUFLEN	(MCLBYTES - RE_ETHER_ALIGN)
+#else
+#define	RE_ETHER_ALIGN	0
+#define	RE_RX_DESC_BUFLEN	MCLBYTES
+#endif
+
+#define	RL_MSI_MESSAGES	1
+
+#define	RL_ADDR_LO(y)		((uint64_t) (y) & 0xFFFFFFFF)
+#define	RL_ADDR_HI(y)		((uint64_t) (y) >> 32)
+
+/*
+ * The number of bits reserved for MSS in RealTek controllers is
+ * 11bits. This limits the maximum interface MTU size in TSO case
+ * as upper stack should not generate TCP segments with MSS greater
+ * than the limit.
+ */
+#define	RL_TSO_MTU		(2047 - ETHER_HDR_LEN - ETHER_CRC_LEN)
+
+/* see comment in dev/re/if_re.c */
+#define	RL_JUMBO_FRAMELEN	7440
+#define	RL_JUMBO_MTU		\
+	(RL_JUMBO_FRAMELEN-ETHER_VLAN_ENCAP_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
+#define	RL_JUMBO_MTU_6K		\
+	((6 * 1024) - ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN)
+#define	RL_JUMBO_MTU_9K		\
+	((9 * 1024) - ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN)
+#define	RL_MTU			\
+	(ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN)
+
+struct rl_txdesc {
+	struct mbuf		*tx_m;
+	bus_dmamap_t		tx_dmamap;
+};
+
+struct rl_rxdesc {
+	struct mbuf		*rx_m;
+	bus_dmamap_t		rx_dmamap;
+	bus_size_t		rx_size;
+};
+
+struct rl_list_data {
+	struct rl_txdesc	rl_tx_desc[RL_TX_DESC_CNT];
+	struct rl_rxdesc	rl_rx_desc[RL_RX_DESC_CNT];
+	struct rl_rxdesc	rl_jrx_desc[RL_RX_JUMBO_DESC_CNT];
+	int			rl_tx_desc_cnt;
+	int			rl_rx_desc_cnt;
+	int			rl_tx_prodidx;
+	int			rl_rx_prodidx;
+	int			rl_tx_considx;
+	int			rl_tx_free;
+	bus_dma_tag_t		rl_tx_mtag;	/* mbuf TX mapping tag */
+	bus_dma_tag_t		rl_rx_mtag;	/* mbuf RX mapping tag */
+	bus_dma_tag_t		rl_jrx_mtag;	/* mbuf RX mapping tag */
+	bus_dmamap_t		rl_rx_sparemap;
+	bus_dmamap_t		rl_jrx_sparemap;
+	bus_dma_tag_t		rl_stag;	/* stats mapping tag */
+	bus_dmamap_t		rl_smap;	/* stats map */
+	struct rl_stats		*rl_stats;
+	bus_addr_t		rl_stats_addr;
+	bus_dma_tag_t		rl_rx_list_tag;
+	bus_dmamap_t		rl_rx_list_map;
+	struct rl_desc		*rl_rx_list;
+	bus_addr_t		rl_rx_list_addr;
+	bus_dma_tag_t		rl_tx_list_tag;
+	bus_dmamap_t		rl_tx_list_map;
+	struct rl_desc		*rl_tx_list;
+	bus_addr_t		rl_tx_list_addr;
+};
+
+enum rl_twist { DONE, CHK_LINK, FIND_ROW, SET_PARAM, RECHK_LONG, RETUNE };
+
+struct rl_softc {
+	struct ifnet		*rl_ifp;	/* interface info */
+	bus_space_handle_t	rl_bhandle;	/* bus space handle */
+	bus_space_tag_t		rl_btag;	/* bus space tag */
+	device_t		rl_dev;
+	struct resource		*rl_res;
+	int			rl_res_id;
+	int			rl_res_type;
+	struct resource		*rl_res_pba;
+	struct resource		*rl_irq[RL_MSI_MESSAGES];
+	void			*rl_intrhand[RL_MSI_MESSAGES];
+	device_t		rl_miibus;
+	bus_dma_tag_t		rl_parent_tag;
+	uint8_t			rl_type;
+	const struct rl_hwrev	*rl_hwrev;
+	uint32_t		rl_macrev;
+	int			rl_eecmd_read;
+	int			rl_eewidth;
+	int			rl_expcap;
+	int			rl_txthresh;
+	bus_size_t		rl_cfg0;
+	bus_size_t		rl_cfg1;
+	bus_size_t		rl_cfg2;
+	bus_size_t		rl_cfg3;
+	bus_size_t		rl_cfg4;
+	bus_size_t		rl_cfg5;
+	struct rl_chain_data	rl_cdata;
+	struct rl_list_data	rl_ldata;
+	struct callout		rl_stat_callout;
+	int			rl_watchdog_timer;
+	struct mtx		rl_mtx;
+	struct mbuf		*rl_head;
+	struct mbuf		*rl_tail;
+	uint32_t		rl_rxlenmask;
+	int			rl_testmode;
+	int			rl_if_flags;
+	int			rl_twister_enable;
+	enum rl_twist		rl_twister;
+	int			rl_twist_row;
+	int			rl_twist_col;
+	int			suspended;	/* 0 = normal  1 = suspended */
+#ifdef DEVICE_POLLING
+	int			rxcycles;
+#endif
+
+	struct task		rl_inttask;
+
+	int			rl_txstart;
+	int			rl_int_rx_act;
+	int			rl_int_rx_mod;
+	uint32_t		rl_flags;
+#define	RL_FLAG_MSI		0x00000001
+#define	RL_FLAG_AUTOPAD		0x00000002
+#define	RL_FLAG_PHYWAKE_PM	0x00000004
+#define	RL_FLAG_PHYWAKE		0x00000008
+#define	RL_FLAG_JUMBOV2		0x00000010
+#define	RL_FLAG_PAR		0x00000020
+#define	RL_FLAG_DESCV2		0x00000040
+#define	RL_FLAG_MACSTAT		0x00000080
+#define	RL_FLAG_FASTETHER	0x00000100
+#define	RL_FLAG_CMDSTOP		0x00000200
+#define	RL_FLAG_MACRESET	0x00000400
+#define	RL_FLAG_MSIX		0x00000800
+#define	RL_FLAG_WOLRXENB	0x00001000
+#define	RL_FLAG_MACSLEEP	0x00002000
+#define	RL_FLAG_WAIT_TXPOLL	0x00004000
+#define	RL_FLAG_CMDSTOP_WAIT_TXQ	0x00008000
+#define	RL_FLAG_WOL_MANLINK	0x00010000
+#define	RL_FLAG_EARLYOFF	0x00020000
+#define	RL_FLAG_8168G_PLUS	0x00040000
+#define	RL_FLAG_PCIE		0x40000000
+#define	RL_FLAG_LINK		0x80000000
+};
+
+#define	RL_LOCK(_sc)		mtx_lock(&(_sc)->rl_mtx)
+#define	RL_UNLOCK(_sc)		mtx_unlock(&(_sc)->rl_mtx)
+#define	RL_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->rl_mtx, MA_OWNED)
+
+/*
+ * register space access macros
+ */
+#define	CSR_WRITE_STREAM_4(sc, reg, val)	\
+	bus_space_write_stream_4(sc->rl_btag, sc->rl_bhandle, reg, val)
+#define	CSR_WRITE_4(sc, reg, val)	\
+	bus_space_write_4(sc->rl_btag, sc->rl_bhandle, reg, val)
+#define	CSR_WRITE_2(sc, reg, val)	\
+	bus_space_write_2(sc->rl_btag, sc->rl_bhandle, reg, val)
+#define	CSR_WRITE_1(sc, reg, val)	\
+	bus_space_write_1(sc->rl_btag, sc->rl_bhandle, reg, val)
+
+#define	CSR_READ_4(sc, reg)		\
+	bus_space_read_4(sc->rl_btag, sc->rl_bhandle, reg)
+#define	CSR_READ_2(sc, reg)		\
+	bus_space_read_2(sc->rl_btag, sc->rl_bhandle, reg)
+#define	CSR_READ_1(sc, reg)		\
+	bus_space_read_1(sc->rl_btag, sc->rl_bhandle, reg)
+
+#define	CSR_BARRIER(sc, reg, length, flags)				\
+	bus_space_barrier(sc->rl_btag, sc->rl_bhandle, reg, length, flags)
+
+#define	CSR_SETBIT_1(sc, offset, val)		\
+	CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) | (val))
+
+#define	CSR_CLRBIT_1(sc, offset, val)		\
+	CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) & ~(val))
+
+#define	CSR_SETBIT_2(sc, offset, val)		\
+	CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) | (val))
+
+#define	CSR_CLRBIT_2(sc, offset, val)		\
+	CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) & ~(val))
+
+#define	CSR_SETBIT_4(sc, offset, val)		\
+	CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) | (val))
+
+#define	CSR_CLRBIT_4(sc, offset, val)		\
+	CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) & ~(val))
+
+#define	RL_TIMEOUT		1000
+#define	RL_PHY_TIMEOUT		2000
+
+/*
+ * General constants that are fun to know.
+ *
+ * RealTek PCI vendor ID
+ */
+#define	RT_VENDORID				0x10EC
+
+/*
+ * RealTek chip device IDs.
+ */
+#define	RT_DEVICEID_8139D			0x8039
+#define	RT_DEVICEID_8129			0x8129
+#define	RT_DEVICEID_8101E			0x8136
+#define	RT_DEVICEID_8138			0x8138
+#define	RT_DEVICEID_8139			0x8139
+#define	RT_DEVICEID_8169SC			0x8167
+#define	RT_DEVICEID_8168			0x8168
+#define	RT_DEVICEID_8169			0x8169
+#define	RT_DEVICEID_8100			0x8100
+
+#define	RT_REVID_8139CPLUS			0x20
+
+/*
+ * Accton PCI vendor ID
+ */
+#define	ACCTON_VENDORID				0x1113
+
+/*
+ * Accton MPX 5030/5038 device ID.
+ */
+#define	ACCTON_DEVICEID_5030			0x1211
+
+/*
+ * Nortel PCI vendor ID
+ */
+#define	NORTEL_VENDORID				0x126C
+
+/*
+ * Delta Electronics Vendor ID.
+ */
+#define	DELTA_VENDORID				0x1500
+
+/*
+ * Delta device IDs.
+ */
+#define	DELTA_DEVICEID_8139			0x1360
+
+/*
+ * Addtron vendor ID.
+ */
+#define	ADDTRON_VENDORID			0x4033
+
+/*
+ * Addtron device IDs.
+ */
+#define	ADDTRON_DEVICEID_8139			0x1360
+
+/*
+ * D-Link vendor ID.
+ */
+#define	DLINK_VENDORID				0x1186
+
+/*
+ * D-Link DFE-530TX+ device ID
+ */
+#define	DLINK_DEVICEID_530TXPLUS		0x1300
+
+/*
+ * D-Link DFE-520TX rev. C1 device ID
+ */
+#define	DLINK_DEVICEID_520TX_REVC1		0x4200
+
+/*
+ * D-Link DFE-5280T device ID
+ */
+#define	DLINK_DEVICEID_528T			0x4300
+#define	DLINK_DEVICEID_530T_REVC		0x4302
+
+/*
+ * D-Link DFE-690TXD device ID
+ */
+#define	DLINK_DEVICEID_690TXD			0x1340
+
+/*
+ * Corega K.K vendor ID
+ */
+#define	COREGA_VENDORID				0x1259
+
+/*
+ * Corega FEther CB-TXD device ID
+ */
+#define	COREGA_DEVICEID_FETHERCBTXD		0xa117
+
+/*
+ * Corega FEtherII CB-TXD device ID
+ */
+#define	COREGA_DEVICEID_FETHERIICBTXD		0xa11e
+
+/*
+ * Corega CG-LAPCIGT device ID
+ */
+#define	COREGA_DEVICEID_CGLAPCIGT		0xc107
+
+/*
+ * Linksys vendor ID
+ */
+#define	LINKSYS_VENDORID			0x1737
+
+/*
+ * Linksys EG1032 device ID
+ */
+#define	LINKSYS_DEVICEID_EG1032			0x1032
+
+/*
+ * Linksys EG1032 rev 3 sub-device ID
+ */
+#define	LINKSYS_SUBDEVICE_EG1032_REV3		0x0024
+
+/*
+ * Peppercon vendor ID
+ */
+#define	PEPPERCON_VENDORID			0x1743
+
+/*
+ * Peppercon ROL-F device ID
+ */
+#define	PEPPERCON_DEVICEID_ROLF			0x8139
+
+/*
+ * Planex Communications, Inc. vendor ID
+ */
+#define	PLANEX_VENDORID				0x14ea
+
+/*
+ * Planex FNW-3603-TX device ID
+ */
+#define	PLANEX_DEVICEID_FNW3603TX		0xab06
+
+/*
+ * Planex FNW-3800-TX device ID
+ */
+#define	PLANEX_DEVICEID_FNW3800TX		0xab07
+
+/*
+ * LevelOne vendor ID
+ */
+#define	LEVEL1_VENDORID				0x018A
+
+/*
+ * LevelOne FPC-0106TX devide ID
+ */
+#define	LEVEL1_DEVICEID_FPC0106TX		0x0106
+
+/*
+ * Compaq vendor ID
+ */
+#define	CP_VENDORID				0x021B
+
+/*
+ * Edimax vendor ID
+ */
+#define	EDIMAX_VENDORID				0x13D1
+
+/*
+ * Edimax EP-4103DL cardbus device ID
+ */
+#define	EDIMAX_DEVICEID_EP4103DL		0xAB06
+
+/* US Robotics vendor ID */
+
+#define	USR_VENDORID		0x16EC
+
+/* US Robotics 997902 device ID */
+
+#define	USR_DEVICEID_997902	0x0116


Property changes on: trunk/sys/dev/rl/if_rlreg.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list