summaryrefslogtreecommitdiffstats
path: root/bsd_eth_drivers/if_re
diff options
context:
space:
mode:
Diffstat (limited to 'bsd_eth_drivers/if_re')
-rw-r--r--bsd_eth_drivers/if_re/.cvsignore1
-rw-r--r--bsd_eth_drivers/if_re/Makefile.am2
-rw-r--r--bsd_eth_drivers/if_re/if_re.c2167
-rw-r--r--bsd_eth_drivers/if_re/if_rl.c2351
-rw-r--r--bsd_eth_drivers/if_re/if_rlreg.h224
5 files changed, 3876 insertions, 869 deletions
diff --git a/bsd_eth_drivers/if_re/.cvsignore b/bsd_eth_drivers/if_re/.cvsignore
new file mode 100644
index 0000000..70845e0
--- /dev/null
+++ b/bsd_eth_drivers/if_re/.cvsignore
@@ -0,0 +1 @@
+Makefile.in
diff --git a/bsd_eth_drivers/if_re/Makefile.am b/bsd_eth_drivers/if_re/Makefile.am
index 5c33e09..e7e2efd 100644
--- a/bsd_eth_drivers/if_re/Makefile.am
+++ b/bsd_eth_drivers/if_re/Makefile.am
@@ -3,7 +3,7 @@ AUTOMAKE_OPTIONS=foreign
include $(top_srcdir)/rtems-pre.am
-libif_re_a_SOURCES = if_re.c
+libif_re_a_SOURCES = if_re.c if_rl.c if_rlreg.h
##EXTRA_libif_re_a_SOURCES =
diff --git a/bsd_eth_drivers/if_re/if_re.c b/bsd_eth_drivers/if_re/if_re.c
index e39a52a..e7a9f49 100644
--- a/bsd_eth_drivers/if_re/if_re.c
+++ b/bsd_eth_drivers/if_re/if_re.c
@@ -32,10 +32,11 @@
#ifdef __rtems__
#include <libbsdport.h>
+#define M_ASSERTPKTHDR(_h)
#endif
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/dev/re/if_re.c,v 1.46.2.39.2.1 2008/10/02 02:57:24 kensmith Exp $");
+__FBSDID("$FreeBSD: src/sys/dev/re/if_re.c,v 1.161 2009/08/24 18:58:13 yongari Exp $");
/*
* RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver
@@ -150,6 +151,17 @@ __FBSDID("$FreeBSD: src/sys/dev/re/if_re.c,v 1.46.2.39.2.1 2008/10/02 02:57:24 k
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+/*
+ * Default to using PIO access for this driver.
+ */
+#define RE_USEIOSPACE
+
+#ifndef __rtems__
+#include <pci/if_rlreg.h>
+#else
+#include "if_rlreg.h"
+#endif
+
MODULE_DEPEND(re, pci, 1, 1, 1);
MODULE_DEPEND(re, ether, 1, 1, 1);
MODULE_DEPEND(re, miibus, 1, 1, 1);
@@ -159,18 +171,16 @@ MODULE_DEPEND(re, miibus, 1, 1, 1);
#ifdef __rtems__
#include <libbsdport_post.h>
+#define TUNABLE_INT(_t, _v)
#endif
-/*
- * Default to using PIO access for this driver.
- */
-#define RE_USEIOSPACE
-
+/* Tunables. */
#ifndef __rtems__
-#include <pci/if_rlreg.h>
-#else
-#include "if_rlreg.h"
+static int msi_disable = 0;
+TUNABLE_INT("hw.re.msi_disable", &msi_disable);
#endif
+static int prefer_iomap = 0;
+TUNABLE_INT("hw.re.prefer_iomap", &prefer_iomap);
#define RE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
@@ -178,39 +188,25 @@ MODULE_DEPEND(re, miibus, 1, 1, 1);
* Various supported device vendors/types and their names.
*/
static struct rl_type re_devs[] = {
- { DLINK_VENDORID, DLINK_DEVICEID_528T, RL_HWREV_8169S,
- "D-Link DGE-528(T) Gigabit Ethernet Adapter" },
- { DLINK_VENDORID, DLINK_DEVICEID_528T, RL_HWREV_8169_8110SB,
- "D-Link DGE-528(T) Rev.B1 Gigabit Ethernet Adapter" },
- { RT_VENDORID, RT_DEVICEID_8139, RL_HWREV_8139CPLUS,
- "RealTek 8139C+ 10/100BaseTX" },
- { RT_VENDORID, RT_DEVICEID_8101E, RL_HWREV_8101E,
- "RealTek 8101E PCIe 10/100baseTX" },
- { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN1,
- "RealTek 8168/8111B PCIe Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN2,
- "RealTek 8168/8111B PCIe Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN3,
- "RealTek 8168/8111B PCIe Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169,
- "RealTek 8169 Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169S,
- "RealTek 8169S Single-chip Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169_8110SB,
- "RealTek 8169SB/8110SB Single-chip Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169_8110SC,
- "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169SC, RL_HWREV_8169_8110SC,
- "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" },
- { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8110S,
- "RealTek 8110S Single-chip Gigabit Ethernet" },
- { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, RL_HWREV_8169S,
- "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" },
- { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, RL_HWREV_8169S,
- "Linksys EG1032 (RTL8169S) Gigabit Ethernet" },
- { USR_VENDORID, USR_DEVICEID_997902, RL_HWREV_8169S,
- "US Robotics 997902 (RTL8169S) Gigabit Ethernet" },
- { 0, 0, 0, NULL }
+ { DLINK_VENDORID, DLINK_DEVICEID_528T, 0,
+ "D-Link DGE-528(T) Gigabit Ethernet Adapter" },
+ { RT_VENDORID, RT_DEVICEID_8139, 0,
+ "RealTek 8139C+ 10/100BaseTX" },
+ { RT_VENDORID, RT_DEVICEID_8101E, 0,
+ "RealTek 8101E/8102E/8102EL PCIe 10/100baseTX" },
+ { RT_VENDORID, RT_DEVICEID_8168, 0,
+ "RealTek 8168/8168B/8168C/8168CP/8168D/8168DP/"
+ "8111B/8111C/8111CP/8111DP PCIe Gigabit Ethernet" },
+ { RT_VENDORID, RT_DEVICEID_8169, 0,
+ "RealTek 8169/8169S/8169SB(L)/8110S/8110SB(L) Gigabit Ethernet" },
+ { RT_VENDORID, RT_DEVICEID_8169SC, 0,
+ "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" },
+ { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, 0,
+ "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" },
+ { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, 0,
+ "Linksys EG1032 (RTL8169S) Gigabit Ethernet" },
+ { USR_VENDORID, USR_DEVICEID_997902, 0,
+ "US Robotics 997902 (RTL8169S) Gigabit Ethernet" }
};
static struct rl_hwrev re_hwrevs[] = {
@@ -226,14 +222,24 @@ static struct rl_hwrev re_hwrevs[] = {
{ RL_HWREV_8169, RL_8169, "8169"},
{ RL_HWREV_8169S, RL_8169, "8169S"},
{ RL_HWREV_8110S, RL_8169, "8110S"},
- { RL_HWREV_8169_8110SB, RL_8169, "8169SB"},
- { RL_HWREV_8169_8110SC, RL_8169, "8169SC"},
+ { RL_HWREV_8169_8110SB, RL_8169, "8169SB/8110SB"},
+ { RL_HWREV_8169_8110SC, RL_8169, "8169SC/8110SC"},
+ { RL_HWREV_8169_8110SBL, RL_8169, "8169SBL/8110SBL"},
+ { RL_HWREV_8169_8110SCE, RL_8169, "8169SC/8110SC"},
{ RL_HWREV_8100, RL_8139, "8100"},
{ RL_HWREV_8101, RL_8139, "8101"},
{ RL_HWREV_8100E, RL_8169, "8100E"},
{ RL_HWREV_8101E, RL_8169, "8101E"},
+ { RL_HWREV_8102E, RL_8169, "8102E"},
+ { RL_HWREV_8102EL, RL_8169, "8102EL"},
+ { RL_HWREV_8102EL_SPIN1, RL_8169, "8102EL"},
{ RL_HWREV_8168_SPIN2, RL_8169, "8168"},
{ RL_HWREV_8168_SPIN3, RL_8169, "8168"},
+ { RL_HWREV_8168C, RL_8169, "8168C/8111C"},
+ { RL_HWREV_8168C_SPIN2, RL_8169, "8168C/8111C"},
+ { RL_HWREV_8168CP, RL_8169, "8168CP/8111CP"},
+ { RL_HWREV_8168D, RL_8169, "8168D/8111D"},
+ { RL_HWREV_8168DP, RL_8169, "8168DP/8111DP"},
{ 0, 0, NULL }
};
@@ -241,26 +247,26 @@ static int re_probe (device_t);
static int re_attach (device_t);
static int re_detach (device_t);
-static int re_encap (struct rl_softc *, struct mbuf **, int *);
+static int re_encap (struct rl_softc *, struct mbuf **);
static void re_dma_map_addr (void *, bus_dma_segment_t *, int, int);
-static void re_dma_map_desc (void *, bus_dma_segment_t *, int,
- bus_size_t, int);
static int re_allocmem (device_t, struct rl_softc *);
-static int re_newbuf (struct rl_softc *, int, struct mbuf *);
+static __inline void re_discard_rxbuf
+ (struct rl_softc *, int);
+static int re_newbuf (struct rl_softc *, int);
static int re_rx_list_init (struct rl_softc *);
static int re_tx_list_init (struct rl_softc *);
#ifdef RE_FIXUP_RX
static __inline void re_fixup_rx
(struct mbuf *);
#endif
-static int re_rxeof (struct rl_softc *);
+static int re_rxeof (struct rl_softc *, int *);
static void re_txeof (struct rl_softc *);
#ifdef DEVICE_POLLING
-static void re_poll (struct ifnet *, enum poll_cmd, int);
-static void re_poll_locked (struct ifnet *, enum poll_cmd, int);
+static int re_poll (struct ifnet *, enum poll_cmd, int);
+static int re_poll_locked (struct ifnet *, enum poll_cmd, int);
#endif
-static void re_intr (void *);
+static int re_intr (void *);
static void re_tick (void *);
static void re_tx_task (void *, int);
static void re_int_task (void *, int);
@@ -278,7 +284,11 @@ static void re_watchdog (struct rl_softc *);
static int re_suspend (device_t);
static int re_resume (device_t);
#endif
-static void re_shutdown (device_t);
+#ifndef __rtems__
+static int re_shutdown (device_t);
+#else
+static void re_shutdown (device_t);
+#endif
#ifndef __rtems__
static int re_ifmedia_upd (struct ifnet *);
static void re_ifmedia_sts (struct ifnet *, struct ifmediareq *);
@@ -287,19 +297,21 @@ static void re_ifmedia_sts (struct ifnet *, struct ifmediareq *);
static void re_eeprom_putbyte (struct rl_softc *, int);
static void re_eeprom_getword (struct rl_softc *, int, u_int16_t *);
static void re_read_eeprom (struct rl_softc *, caddr_t, int, int);
-#ifndef __rtems__
static int re_gmii_readreg (device_t, int, int);
-#endif
static int re_gmii_writereg (device_t, int, int, int);
-#ifndef __rtems__
static int re_miibus_readreg (device_t, int, int);
static int re_miibus_writereg (device_t, int, int, int);
+#ifndef __rtems__
static void re_miibus_statchg (device_t);
#endif
-static void re_setmulti (struct rl_softc *);
+static void re_set_rxmode (struct rl_softc *);
static void re_reset (struct rl_softc *);
+#ifndef __rtems__
+static void re_setwol (struct rl_softc *);
+static void re_clrwol (struct rl_softc *);
+#endif
#ifdef RE_DIAG
static int re_diag (struct rl_softc *);
@@ -344,36 +356,16 @@ static driver_t re_driver = {
static devclass_t re_devclass;
DRIVER_MODULE(re, pci, re_driver, re_devclass, 0, 0);
-DRIVER_MODULE(re, cardbus, re_driver, re_devclass, 0, 0);
DRIVER_MODULE(miibus, re, miibus_driver, miibus_devclass, 0, 0);
#else
-static int
-re_irq_check_dis(device_t d)
-{
- // struct re_softc *sc = device_get_softc(d);
- printk( "check_dis\n" );
- return 0;
-}
-
-static void
-re_irq_en(device_t d)
-{
- // struct re_softc *sc = device_get_softc(d);
- /* This can be called from IRQ context -- since all register accesses
- * involve RAP we must take care to preserve it across this routine!
- */
- printk( "irq_en\n" );
-}
-
-
static device_method_t re_methods = {
probe: re_probe,
attach: re_attach,
shutdown: re_shutdown,
detach: re_detach,
- irq_check_dis: re_irq_check_dis,
- irq_en: re_irq_en,
+ irq_check_dis: 0,
+ irq_en: 0,
};
driver_t libbsdport_re_driver = {
@@ -383,6 +375,45 @@ driver_t libbsdport_re_driver = {
sizeof(struct rl_softc)
};
+static int
+mdio_r(int phy, void *uarg, unsigned reg, uint32_t *pval)
+{
+struct rl_softc *sc = uarg;
+
+ if ( phy != 0 )
+ return EINVAL;
+
+ *pval = (uint32_t) re_miibus_readreg(sc->rl_dev, phy, reg);
+
+ return 0;
+}
+
+static int
+mdio_w(int phy, void *uarg, unsigned reg, uint32_t val)
+{
+struct rl_softc *sc = uarg;
+
+ if ( phy != 0 )
+ return EINVAL;
+
+ re_miibus_writereg(sc->rl_dev, phy, reg, val);
+
+ return 0;
+}
+
+struct rtems_mdio_info re_mdio_100 = {
+ mdio_r : mdio_r,
+ mdio_w : mdio_w,
+ has_gmii : 0
+};
+
+struct rtems_mdio_info re_mdio_1000 = {
+ mdio_r : mdio_r,
+ mdio_w : mdio_w,
+ has_gmii : 1
+};
+
+#define RE_MDIO(sc) ((sc)->rl_type == RL_8169 ? &re_mdio_1000 : & re_mdio_100)
#endif
@@ -398,11 +429,9 @@ driver_t libbsdport_re_driver = {
* Send a read command and address to the EEPROM, check for ACK.
*/
static void
-re_eeprom_putbyte(sc, addr)
- struct rl_softc *sc;
- int addr;
+re_eeprom_putbyte(struct rl_softc *sc, int addr)
{
- register int d, i;
+ int d, i;
d = addr | (RL_9346_READ << sc->rl_eewidth);
@@ -422,20 +451,15 @@ re_eeprom_putbyte(sc, addr)
EE_CLR(RL_EE_CLK);
DELAY(100);
}
-
- return;
}
/*
* Read a word of data stored in the EEPROM at address 'addr.'
*/
static void
-re_eeprom_getword(sc, addr, dest)
- struct rl_softc *sc;
- int addr;
- u_int16_t *dest;
+re_eeprom_getword(struct rl_softc *sc, int addr, u_int16_t *dest)
{
- register int i;
+ int i;
u_int16_t word = 0;
/*
@@ -456,19 +480,13 @@ re_eeprom_getword(sc, addr, dest)
}
*dest = word;
-
- return;
}
/*
* Read a sequence of words from the EEPROM.
*/
static void
-re_read_eeprom(sc, dest, off, cnt)
- struct rl_softc *sc;
- caddr_t dest;
- int off;
- int cnt;
+re_read_eeprom(struct rl_softc *sc, caddr_t dest, int off, int cnt)
{
int i;
u_int16_t word = 0, *ptr;
@@ -486,15 +504,10 @@ re_read_eeprom(sc, dest, off, cnt)
}
CSR_CLRBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM);
-
- return;
}
-#ifndef __rtems__
static int
-re_gmii_readreg(dev, phy, reg)
- device_t dev;
- int phy, reg;
+re_gmii_readreg(device_t dev, int phy, int reg)
{
struct rl_softc *sc;
u_int32_t rval;
@@ -515,26 +528,23 @@ re_gmii_readreg(dev, phy, reg)
CSR_WRITE_4(sc, RL_PHYAR, reg << 16);
DELAY(1000);
- for (i = 0; i < RL_TIMEOUT; i++) {
+ for (i = 0; i < RL_PHY_TIMEOUT; i++) {
rval = CSR_READ_4(sc, RL_PHYAR);
if (rval & RL_PHYAR_BUSY)
break;
DELAY(100);
}
- if (i == RL_TIMEOUT) {
+ if (i == RL_PHY_TIMEOUT) {
device_printf(sc->rl_dev, "PHY read failed\n");
return (0);
}
return (rval & RL_PHYAR_PHYDATA);
}
-#endif
static int
-re_gmii_writereg(dev, phy, reg, data)
- device_t dev;
- int phy, reg, data;
+re_gmii_writereg(device_t dev, int phy, int reg, int data)
{
struct rl_softc *sc;
u_int32_t rval;
@@ -546,14 +556,14 @@ re_gmii_writereg(dev, phy, reg, data)
(data & RL_PHYAR_PHYDATA) | RL_PHYAR_BUSY);
DELAY(1000);
- for (i = 0; i < RL_TIMEOUT; i++) {
+ for (i = 0; i < RL_PHY_TIMEOUT; i++) {
rval = CSR_READ_4(sc, RL_PHYAR);
if (!(rval & RL_PHYAR_BUSY))
break;
DELAY(100);
}
- if (i == RL_TIMEOUT) {
+ if (i == RL_PHY_TIMEOUT) {
device_printf(sc->rl_dev, "PHY write failed\n");
return (0);
}
@@ -561,11 +571,8 @@ re_gmii_writereg(dev, phy, reg, data)
return (0);
}
-#ifndef __rtems__
static int
-re_miibus_readreg(dev, phy, reg)
- device_t dev;
- int phy, reg;
+re_miibus_readreg(device_t dev, int phy, int reg)
{
struct rl_softc *sc;
u_int16_t rval = 0;
@@ -621,13 +628,9 @@ re_miibus_readreg(dev, phy, reg)
}
return (rval);
}
-#endif
-#ifndef __rtems__
static int
-re_miibus_writereg(dev, phy, reg, data)
- device_t dev;
- int phy, reg, data;
+re_miibus_writereg(device_t dev, int phy, int reg, int data)
{
struct rl_softc *sc;
u_int16_t re8139_reg = 0;
@@ -675,42 +678,67 @@ re_miibus_writereg(dev, phy, reg, data)
CSR_WRITE_2(sc, re8139_reg, data);
return (0);
}
-#endif
#ifndef __rtems__
static void
-re_miibus_statchg(dev)
- device_t dev;
+re_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;
+ case IFM_1000_T:
+ if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0)
+ break;
+ sc->rl_flags |= RL_FLAG_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+ /*
+ * RealTek controllers does not provide any interface to
+ * Tx/Rx MACs for resolved speed, duplex and flow-control
+ * parameters.
+ */
}
#endif
/*
- * Program the 64-bit multicast hash filter.
+ * Set the RX configuration and 64-bit multicast hash filter.
*/
static void
-re_setmulti(sc)
- struct rl_softc *sc;
+re_set_rxmode(struct rl_softc *sc)
{
struct ifnet *ifp;
#ifndef __rtems__
- int h = 0;
struct ifmultiaddr *ifma;
#endif
- u_int32_t hashes[2] = { 0, 0 };
- u_int32_t rxfilt;
- int mcnt = 0;
- u_int32_t hwrev;
+ uint32_t hashes[2] = { 0, 0 };
+ uint32_t h, rxfilt;
RL_LOCK_ASSERT(sc);
ifp = sc->rl_ifp;
+ rxfilt = RL_RXCFG_CONFIG | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_BROAD;
- rxfilt = CSR_READ_4(sc, RL_RXCFG);
- rxfilt &= ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_MULTI);
- if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
if (ifp->if_flags & IFF_PROMISC)
rxfilt |= RL_RXCFG_RX_ALLPHYS;
/*
@@ -719,19 +747,12 @@ re_setmulti(sc)
* promiscuous mode.
*/
rxfilt |= RL_RXCFG_RX_MULTI;
- CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
- CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF);
- CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF);
- return;
+ hashes[0] = hashes[1] = 0xffffffff;
+ goto done;
}
- /* first, zot all the existing hash bits */
- CSR_WRITE_4(sc, RL_MAR0, 0);
- CSR_WRITE_4(sc, RL_MAR4, 0);
-
- /* now program new ones */
#ifndef __rtems__
- IF_ADDR_LOCK(ifp);
+ if_maddr_rlock(ifp);
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
@@ -741,48 +762,50 @@ re_setmulti(sc)
hashes[0] |= (1 << h);
else
hashes[1] |= (1 << (h - 32));
- mcnt++;
}
- IF_ADDR_UNLOCK(ifp);
+ if_maddr_runlock(ifp);
+#else
+ {
+ /* UNTESTED */
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ ETHER_FIRST_MULTI(step, (struct arpcom*)ifp, enm);
+ while ( enm != NULL ) {
+ h = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+ }
#endif
-
- if (mcnt)
+
+ if (hashes[0] != 0 || hashes[1] != 0) {
+ /*
+ * For some unfathomable reason, RealTek decided to
+ * reverse the order of the multicast hash registers
+ * in the PCI Express parts. This means we have to
+ * write the hash pattern in reverse order for those
+ * devices.
+ */
+ if ((sc->rl_flags & RL_FLAG_PCIE) != 0) {
+ h = bswap32(hashes[0]);
+ hashes[0] = bswap32(hashes[1]);
+ hashes[1] = h;
+ }
rxfilt |= RL_RXCFG_RX_MULTI;
- else
- rxfilt &= ~RL_RXCFG_RX_MULTI;
+ }
+done:
+ CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
-
- /*
- * For some unfathomable reason, RealTek decided to reverse
- * the order of the multicast hash registers in the PCI Express
- * parts. This means we have to write the hash pattern in reverse
- * order for those devices.
- */
-
- hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
-
- switch (hwrev) {
- case RL_HWREV_8100E:
- case RL_HWREV_8101E:
- case RL_HWREV_8168_SPIN1:
- case RL_HWREV_8168_SPIN2:
- case RL_HWREV_8168_SPIN3:
- CSR_WRITE_4(sc, RL_MAR0, bswap32(hashes[1]));
- CSR_WRITE_4(sc, RL_MAR4, bswap32(hashes[0]));
- break;
- default:
- CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
- CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
- break;
- }
}
static void
-re_reset(sc)
- struct rl_softc *sc;
+re_reset(struct rl_softc *sc)
{
- register int i;
+ int i;
RL_LOCK_ASSERT(sc);
@@ -796,7 +819,10 @@ re_reset(sc)
if (i == RL_TIMEOUT)
device_printf(sc->rl_dev, "reset never completed!\n");
- CSR_WRITE_1(sc, 0x82, 1);
+ if ((sc->rl_flags & RL_FLAG_MACRESET) != 0)
+ CSR_WRITE_1(sc, 0x82, 1);
+ if (sc->rl_hwrev == RL_HWREV_8169S)
+ re_gmii_writereg(sc->rl_dev, 1, 0x0b, 0);
}
#ifdef RE_DIAG
@@ -822,8 +848,7 @@ re_reset(sc)
*/
static int
-re_diag(sc)
- struct rl_softc *sc;
+re_diag(struct rl_softc *sc)
{
struct ifnet *ifp = sc->rl_ifp;
struct mbuf *m0;
@@ -853,9 +878,8 @@ re_diag(sc)
ifp->if_flags |= IFF_PROMISC;
sc->rl_testmode = 1;
- re_reset(sc);
re_init_locked(sc);
- sc->rl_link = 1;
+ sc->rl_flags |= RL_FLAG_LINK;
if (sc->rl_type == RL_8169)
phyaddr = 1;
else
@@ -921,14 +945,14 @@ re_diag(sc)
bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
sc->rl_ldata.rl_rx_list_map,
BUS_DMASYNC_POSTREAD);
- bus_dmamap_sync(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[0],
- BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[0]);
-
- m0 = sc->rl_ldata.rl_rx_mbuf[0];
- sc->rl_ldata.rl_rx_mbuf[0] = NULL;
+ bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag,
+ sc->rl_ldata.rl_rx_desc[0].rx_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag,
+ sc->rl_ldata.rl_rx_desc[0].rx_dmamap);
+
+ m0 = sc->rl_ldata.rl_rx_desc[0].rx_m;
+ sc->rl_ldata.rl_rx_desc[0].rx_m = NULL;
eh = mtod(m0, struct ether_header *);
cur_rx = &sc->rl_ldata.rl_rx_list[0];
@@ -966,7 +990,7 @@ done:
/* Turn interface off, release resources */
sc->rl_testmode = 0;
- sc->rl_link = 0;
+ sc->rl_flags &= ~RL_FLAG_LINK;
ifp->if_flags &= ~IFF_PROMISC;
re_stop(sc);
if (m0 != NULL)
@@ -984,137 +1008,45 @@ done:
* IDs against our list and return a device name if we find a match.
*/
static int
-re_probe(dev)
- device_t dev;
+re_probe(device_t dev)
{
struct rl_type *t;
- struct rl_softc *sc;
- int rid;
- u_int32_t hwrev;
-
- t = re_devs;
- sc = device_get_softc(dev);
-
- while (t->rl_name != NULL) {
- if ((pci_get_vendor(dev) == t->rl_vid) &&
- (pci_get_device(dev) == t->rl_did)) {
+ uint16_t devid, vendor;
+ uint16_t revid, sdevid;
+ int i;
+
+ vendor = pci_get_vendor(dev);
+ devid = pci_get_device(dev);
+ revid = pci_get_revid(dev);
+ sdevid = pci_get_subdevice(dev);
+
+ if (vendor == LINKSYS_VENDORID && devid == LINKSYS_DEVICEID_EG1032) {
+ if (sdevid != LINKSYS_SUBDEVICE_EG1032_REV3) {
/*
* Only attach to rev. 3 of the Linksys EG1032 adapter.
- * Rev. 2 i supported by sk(4).
+ * Rev. 2 is supported by sk(4).
*/
- if ((t->rl_vid == LINKSYS_VENDORID) &&
- (t->rl_did == LINKSYS_DEVICEID_EG1032) &&
- (pci_get_subdevice(dev) !=
- LINKSYS_SUBDEVICE_EG1032_REV3)) {
- t++;
- continue;
- }
-
- /*
- * Temporarily map the I/O space
- * so we can read the chip ID register.
- */
- rid = RL_RID;
- sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid,
- RF_ACTIVE);
- if (sc->rl_res == NULL) {
- device_printf(dev,
- "couldn't map ports/memory\n");
- return (ENXIO);
- }
- sc->rl_btag = rman_get_bustag(sc->rl_res);
- sc->rl_bhandle = rman_get_bushandle(sc->rl_res);
- hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
- bus_release_resource(dev, RL_RES,
- RL_RID, sc->rl_res);
- if (t->rl_basetype == hwrev) {
- device_set_desc(dev, t->rl_name);
- return (BUS_PROBE_DEFAULT);
- }
+ return (ENXIO);
}
- t++;
}
- return (ENXIO);
-}
-
-/*
- * This routine takes the segment list provided as the result of
- * a bus_dma_map_load() operation and assigns the addresses/lengths
- * to RealTek DMA descriptors. This can be called either by the RX
- * code or the TX code. In the RX case, we'll probably wind up mapping
- * at most one segment. For the TX case, there could be any number of
- * segments since TX packets may span multiple mbufs. In either case,
- * if the number of segments is larger than the rl_maxsegs limit
- * specified by the caller, we abort the mapping operation. Sadly,
- * whoever designed the buffer mapping API did not provide a way to
- * return an error from here, so we have to fake it a bit.
- */
-
-static void
-re_dma_map_desc(arg, segs, nseg, mapsize, error)
- void *arg;
- bus_dma_segment_t *segs;
- int nseg;
- bus_size_t mapsize;
- int error;
-{
- struct rl_dmaload_arg *ctx;
- struct rl_desc *d = NULL;
- int i = 0, idx;
- u_int32_t cmdstat;
- int totlen = 0;
-
- if (error)
- return;
-
- ctx = arg;
-
- /* Signal error to caller if there's too many segments */
- if (nseg > ctx->rl_maxsegs) {
- ctx->rl_maxsegs = 0;
- return;
+ if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) {
+ if (revid != 0x20) {
+ /* 8139, let rl(4) take care of this device. */
+ printf ("need rl driver: %0x %0x %0x\n", vendor, devid, revid);
+ return (ENXIO);
+ }
}
- /*
- * Map the segment array into descriptors. Note that we set the
- * start-of-frame and end-of-frame markers for either TX or RX, but
- * they really only have meaning in the TX case. (In the RX case,
- * it's the chip that tells us where packets begin and end.)
- * We also keep track of the end of the ring and set the
- * end-of-ring bits as needed, and we set the ownership bits
- * in all except the very first descriptor. (The caller will
- * set this descriptor later when it start transmission or
- * reception.)
- */
- idx = ctx->rl_idx;
- for (;;) {
- d = &ctx->rl_ring[idx];
- if (le32toh(d->rl_cmdstat) & RL_RDESC_STAT_OWN) {
- ctx->rl_maxsegs = 0;
- return;
+ t = re_devs;
+ for (i = 0; i < sizeof(re_devs) / sizeof(re_devs[0]); i++, t++) {
+ if (vendor == t->rl_vid && devid == t->rl_did) {
+ device_set_desc(dev, t->rl_name);
+ return (BUS_PROBE_DEFAULT);
}
- cmdstat = segs[i].ds_len;
- totlen += segs[i].ds_len;
- d->rl_vlanctl = 0;
- d->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr));
- d->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr));
- if (i == 0)
- cmdstat |= RL_TDESC_CMD_SOF;
- else
- cmdstat |= RL_TDESC_CMD_OWN;
- if (idx == (RL_RX_DESC_CNT - 1))
- cmdstat |= RL_TDESC_CMD_EOR;
- d->rl_cmdstat = htole32(cmdstat | ctx->rl_flags);
- i++;
- if (i == nseg)
- break;
- RL_DESC_INC(idx);
}
- d->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF);
- ctx->rl_maxsegs = nseg;
- ctx->rl_idx = idx;
+ return (ENXIO);
}
/*
@@ -1122,11 +1054,7 @@ re_dma_map_desc(arg, segs, nseg, mapsize, error)
*/
static void
-re_dma_map_addr(arg, segs, nseg, error)
- void *arg;
- bus_dma_segment_t *segs;
- int nseg;
- int error;
+re_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
bus_addr_t *addr;
@@ -1139,25 +1067,54 @@ re_dma_map_addr(arg, segs, nseg, error)
}
static int
-re_allocmem(dev, sc)
- device_t dev;
- struct rl_softc *sc;
+re_allocmem(device_t dev, struct rl_softc *sc)
{
+ bus_size_t rx_list_size, tx_list_size;
int error;
- int nseg;
int i;
+ rx_list_size = sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc);
+ tx_list_size = sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc);
+
+ /*
+ * Allocate the parent bus DMA tag appropriate for PCI.
+ * In order to use DAC, RL_CPLUSCMD_PCI_DAC bit of RL_CPLUS_CMD
+ * register should be set. However some RealTek chips are known
+ * to be buggy on DAC handling, therefore disable DAC by limiting
+ * DMA address space to 32bit. PCIe variants of RealTek chips
+ * may not have the limitation but I took safer path.
+ */
+ error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->rl_parent_tag);
+ if (error) {
+ device_printf(dev, "could not allocate parent DMA tag\n");
+ return (error);
+ }
+
+ /*
+ * Allocate map for TX mbufs.
+ */
+ error = bus_dma_tag_create(sc->rl_parent_tag, 1, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL,
+ NULL, MCLBYTES * RL_NTXSEGS, RL_NTXSEGS, 4096, 0,
+ NULL, NULL, &sc->rl_ldata.rl_tx_mtag);
+ if (error) {
+ device_printf(dev, "could not allocate TX DMA tag\n");
+ return (error);
+ }
+
/*
* Allocate map for RX mbufs.
*/
- nseg = 32;
- error = bus_dma_tag_create(sc->rl_parent_tag, ETHER_ALIGN, 0,
- BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
- NULL, MCLBYTES * nseg, nseg, MCLBYTES, BUS_DMA_ALLOCNOW,
- NULL, NULL, &sc->rl_ldata.rl_mtag);
+
+ error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t), 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->rl_ldata.rl_rx_mtag);
if (error) {
- device_printf(dev, "could not allocate dma tag\n");
- return (ENOMEM);
+ device_printf(dev, "could not allocate RX DMA tag\n");
+ return (error);
}
/*
@@ -1165,36 +1122,44 @@ re_allocmem(dev, sc)
*/
error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN,
0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
- NULL, RL_TX_LIST_SZ, 1, RL_TX_LIST_SZ, 0,
+ NULL, tx_list_size, 1, tx_list_size, 0,
NULL, NULL, &sc->rl_ldata.rl_tx_list_tag);
if (error) {
- device_printf(dev, "could not allocate dma tag\n");
- return (ENOMEM);
+ device_printf(dev, "could not allocate TX DMA ring tag\n");
+ return (error);
}
/* Allocate DMA'able memory for the TX ring */
error = bus_dmamem_alloc(sc->rl_ldata.rl_tx_list_tag,
- (void **)&sc->rl_ldata.rl_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
+ (void **)&sc->rl_ldata.rl_tx_list,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
&sc->rl_ldata.rl_tx_list_map);
- if (error)
- return (ENOMEM);
+ if (error) {
+ device_printf(dev, "could not allocate TX DMA ring\n");
+ return (error);
+ }
/* Load the map for the TX ring. */
+ sc->rl_ldata.rl_tx_list_addr = 0;
error = bus_dmamap_load(sc->rl_ldata.rl_tx_list_tag,
- sc->rl_ldata.rl_tx_list_map, (caddr_t) sc->rl_ldata.rl_tx_list,
- RL_TX_LIST_SZ, re_dma_map_addr,
+ sc->rl_ldata.rl_tx_list_map, sc->rl_ldata.rl_tx_list,
+ tx_list_size, re_dma_map_addr,
&sc->rl_ldata.rl_tx_list_addr, BUS_DMA_NOWAIT);
+ if (error != 0 || sc->rl_ldata.rl_tx_list_addr == 0) {
+ device_printf(dev, "could not load TX DMA ring\n");
+ return (ENOMEM);
+ }
/* Create DMA maps for TX buffers */
- for (i = 0; i < RL_TX_DESC_CNT; i++) {
- error = bus_dmamap_create(sc->rl_ldata.rl_mtag, 0,
- &sc->rl_ldata.rl_tx_dmamap[i]);
+ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
+ error = bus_dmamap_create(sc->rl_ldata.rl_tx_mtag, 0,
+ &sc->rl_ldata.rl_tx_desc[i].tx_dmamap);
if (error) {
- device_printf(dev, "can't create DMA map for TX\n");
- return (ENOMEM);
+ device_printf(dev, "could not create DMA map for TX\n");
+ return (error);
}
}
@@ -1203,36 +1168,50 @@ re_allocmem(dev, sc)
*/
error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN,
0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
- NULL, RL_RX_LIST_SZ, 1, RL_RX_LIST_SZ, 0,
+ NULL, rx_list_size, 1, rx_list_size, 0,
NULL, NULL, &sc->rl_ldata.rl_rx_list_tag);
if (error) {
- device_printf(dev, "could not allocate dma tag\n");
- return (ENOMEM);
+ device_printf(dev, "could not create RX DMA ring tag\n");
+ return (error);
}
/* Allocate DMA'able memory for the RX ring */
error = bus_dmamem_alloc(sc->rl_ldata.rl_rx_list_tag,
- (void **)&sc->rl_ldata.rl_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
+ (void **)&sc->rl_ldata.rl_rx_list,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
&sc->rl_ldata.rl_rx_list_map);
- if (error)
- return (ENOMEM);
+ if (error) {
+ device_printf(dev, "could not allocate RX DMA ring\n");
+ return (error);
+ }
/* Load the map for the RX ring. */
+ sc->rl_ldata.rl_rx_list_addr = 0;
error = bus_dmamap_load(sc->rl_ldata.rl_rx_list_tag,
- sc->rl_ldata.rl_rx_list_map, (caddr_t) sc->rl_ldata.rl_rx_list,
- RL_RX_LIST_SZ, re_dma_map_addr,
+ sc->rl_ldata.rl_rx_list_map, sc->rl_ldata.rl_rx_list,
+ rx_list_size, re_dma_map_addr,
&sc->rl_ldata.rl_rx_list_addr, BUS_DMA_NOWAIT);
+ if (error != 0 || sc->rl_ldata.rl_rx_list_addr == 0) {
+ device_printf(dev, "could not load RX DMA ring\n");
+ return (ENOMEM);
+ }
/* Create DMA maps for RX buffers */
- for (i = 0; i < RL_RX_DESC_CNT; i++) {
- error = bus_dmamap_create(sc->rl_ldata.rl_mtag, 0,
- &sc->rl_ldata.rl_rx_dmamap[i]);
+ error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0,
+ &sc->rl_ldata.rl_rx_sparemap);
+ if (error) {
+ device_printf(dev, "could not create spare DMA map for RX\n");
+ return (error);
+ }
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0,
+ &sc->rl_ldata.rl_rx_desc[i].rx_dmamap);
if (error) {
- device_printf(dev, "can't create DMA map for RX\n");
- return (ENOMEM);
+ device_printf(dev, "could not create DMA map for RX\n");
+ return (error);
}
}
@@ -1244,8 +1223,7 @@ re_allocmem(dev, sc)
* setup and ethernet/BPF attach.
*/
static int
-re_attach(dev)
- device_t dev;
+re_attach(device_t dev)
{
u_char eaddr[ETHER_ADDR_LEN];
u_int16_t as[ETHER_ADDR_LEN / 2];
@@ -1253,8 +1231,14 @@ re_attach(dev)
struct ifnet *ifp;
struct rl_hwrev *hw_rev;
int hwrev;
- u_int16_t re_did = 0;
+ u_int16_t devid, re_did = 0;
int error = 0, rid, i;
+#ifndef __rtems__
+ int msic, reg;
+#else
+ int msic;
+#endif
+ uint8_t cfg;
sc = device_get_softc(dev);
sc->rl_dev = dev;
@@ -1268,10 +1252,32 @@ re_attach(dev)
*/
pci_enable_busmaster(dev);
- rid = RL_RID;
- sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid,
- RF_ACTIVE);
-
+ devid = pci_get_device(dev);
+ /*
+ * Prefer memory space register mapping over IO space.
+ * Because RTL8169SC does not seem to work when memory mapping
+ * is used always activate io mapping.
+ */
+ if (devid == RT_DEVICEID_8169SC)
+ prefer_iomap = 1;
+ if (prefer_iomap == 0) {
+ sc->rl_res_id = PCIR_BAR(1);
+ sc->rl_res_type = SYS_RES_MEMORY;
+ /* RTL8168/8101E seems to use different BARs. */
+ if (devid == RT_DEVICEID_8168 || devid == RT_DEVICEID_8101E)
+ sc->rl_res_id = PCIR_BAR(2);
+ } else {
+ 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 (sc->rl_res == NULL && prefer_iomap == 0) {
+ 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 (sc->rl_res == NULL) {
device_printf(dev, "couldn't map ports/memory\n");
error = ENXIO;
@@ -1281,15 +1287,66 @@ re_attach(dev)
sc->rl_btag = rman_get_bustag(sc->rl_res);
sc->rl_bhandle = rman_get_bushandle(sc->rl_res);
+ msic = 0;
+#ifndef __rtems__
+ if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
+ sc->rl_flags |= RL_FLAG_PCIE;
+ msic = pci_msi_count(dev);
+ if (bootverbose)
+ device_printf(dev, "MSI count : %d\n", msic);
+ }
+ if (msic > 0 && msi_disable == 0) {
+ msic = 1;
+ if (pci_alloc_msi(dev, &msic) == 0) {
+ if (msic == RL_MSI_MESSAGES) {
+ device_printf(dev, "Using %d MSI messages\n",
+ msic);
+ sc->rl_flags |= RL_FLAG_MSI;
+ /* Explicitly set MSI enable bit. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+ cfg = CSR_READ_1(sc, RL_CFG2);
+ cfg |= RL_CFG2_MSI;
+ CSR_WRITE_1(sc, RL_CFG2, cfg);
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+ } else
+ pci_release_msi(dev);
+ }
+ }
+#endif
+
/* Allocate interrupt */
- rid = 0;
- sc->rl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
- RF_SHAREABLE | RF_ACTIVE);
+ if ((sc->rl_flags & RL_FLAG_MSI) == 0) {
+ 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 allocate IRQ resources\n");
+ error = ENXIO;
+ goto fail;
+ }
+ } else {
+ for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) {
+ sc->rl_irq[i] = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->rl_irq[i] == NULL) {
+ device_printf(dev,
+ "couldn't llocate IRQ resources for "
+ "message %d\n", rid);
+ error = ENXIO;
+ goto fail;
+ }
+ }
+ }
- if (sc->rl_irq == NULL) {
- device_printf(dev, "couldn't map interrupt\n");
- error = ENXIO;
- goto fail;
+ if ((sc->rl_flags & RL_FLAG_MSI) == 0) {
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+ cfg = CSR_READ_1(sc, RL_CFG2);
+ if ((cfg & RL_CFG2_MSI) != 0) {
+ device_printf(dev, "turning off MSI enable bit.\n");
+ cfg &= ~RL_CFG2_MSI;
+ CSR_WRITE_1(sc, RL_CFG2, cfg);
+ }
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
}
/* Reset the adapter. */
@@ -1298,51 +1355,144 @@ re_attach(dev)
RL_UNLOCK(sc);
hw_rev = re_hwrevs;
- hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
+ hwrev = CSR_READ_4(sc, RL_TXCFG);
+ switch (hwrev & 0x70000000) {
+ case 0x00000000:
+ case 0x10000000:
+ device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0xfc800000);
+ hwrev &= (RL_TXCFG_HWREV | 0x80000000);
+ break;
+ default:
+ device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0x7c800000);
+ hwrev &= RL_TXCFG_HWREV;
+ break;
+ }
+ device_printf(dev, "MAC rev. 0x%08x\n", hwrev & 0x00700000);
while (hw_rev->rl_desc != NULL) {
if (hw_rev->rl_rev == hwrev) {
sc->rl_type = hw_rev->rl_type;
+ sc->rl_hwrev = hw_rev->rl_rev;
break;
}
hw_rev++;
}
+ if (hw_rev->rl_desc == NULL) {
+ device_printf(dev, "Unknown H/W revision: 0x%08x\n", hwrev);
+ error = ENXIO;
+ goto fail;
+ }
- sc->rl_eewidth = RL_9356_ADDR_LEN;
- re_read_eeprom(sc, (caddr_t)&re_did, 0, 1);
- if (re_did != 0x8129)
- sc->rl_eewidth = RL_9346_ADDR_LEN;
+ switch (hw_rev->rl_rev) {
+ case RL_HWREV_8139CPLUS:
+ sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_FASTETHER |
+ RL_FLAG_AUTOPAD;
+ break;
+ case RL_HWREV_8100E:
+ case RL_HWREV_8101E:
+ sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE |
+ RL_FLAG_FASTETHER;
+ break;
+ case RL_HWREV_8102E:
+ case RL_HWREV_8102EL:
+ case RL_HWREV_8102EL_SPIN1:
+ sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE |
+ RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
+ RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD;
+ break;
+ case RL_HWREV_8168_SPIN1:
+ case RL_HWREV_8168_SPIN2:
+ sc->rl_flags |= RL_FLAG_WOLRXENB;
+ /* FALLTHROUGH */
+ case RL_HWREV_8168_SPIN3:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_MACSTAT;
+ break;
+ case RL_HWREV_8168C_SPIN2:
+ sc->rl_flags |= RL_FLAG_MACSLEEP;
+ /* FALLTHROUGH */
+ case RL_HWREV_8168C:
+ if ((hwrev & 0x00700000) == 0x00200000)
+ sc->rl_flags |= RL_FLAG_MACSLEEP;
+ /* FALLTHROUGH */
+ case RL_HWREV_8168CP:
+ case RL_HWREV_8168D:
+ case RL_HWREV_8168DP:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
+ RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
+ RL_FLAG_AUTOPAD;
+ /*
+ * These controllers support jumbo frame but it seems
+ * that enabling it requires touching additional magic
+ * registers. Depending on MAC revisions some
+ * controllers need to disable checksum offload. So
+ * disable jumbo frame until I have better idea what
+ * it really requires to make it support.
+ * RTL8168C/CP : supports up to 6KB jumbo frame.
+ * RTL8111C/CP : supports up to 9KB jumbo frame.
+ */
+ sc->rl_flags |= RL_FLAG_NOJUMBO;
+ break;
+ case RL_HWREV_8169_8110SB:
+ case RL_HWREV_8169_8110SBL:
+ case RL_HWREV_8169_8110SC:
+ case RL_HWREV_8169_8110SCE:
+ sc->rl_flags |= RL_FLAG_PHYWAKE;
+ /* FALLTHROUGH */
+ case RL_HWREV_8169:
+ case RL_HWREV_8169S:
+ case RL_HWREV_8110S:
+ sc->rl_flags |= RL_FLAG_MACRESET;
+ break;
+ default:
+ break;
+ }
- /*
- * Get station address from the EEPROM.
- */
- re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3);
- for (i = 0; i < ETHER_ADDR_LEN / 2; i++)
- as[i] = le16toh(as[i]);
- bcopy(as, eaddr, sizeof(eaddr));
+ /* Enable PME. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+ cfg = CSR_READ_1(sc, RL_CFG1);
+ cfg |= RL_CFG1_PME;
+ CSR_WRITE_1(sc, RL_CFG1, cfg);
+ cfg = CSR_READ_1(sc, RL_CFG5);
+ cfg &= RL_CFG5_PME_STS;
+ CSR_WRITE_1(sc, RL_CFG5, cfg);
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ if ((sc->rl_flags & RL_FLAG_PAR) != 0) {
+ /*
+ * XXX Should have a better way to extract station
+ * address from EEPROM.
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i);
+ } else {
+ sc->rl_eewidth = RL_9356_ADDR_LEN;
+ re_read_eeprom(sc, (caddr_t)&re_did, 0, 1);
+ if (re_did != 0x8129)
+ sc->rl_eewidth = RL_9346_ADDR_LEN;
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3);
+ for (i = 0; i < ETHER_ADDR_LEN / 2; i++)
+ as[i] = le16toh(as[i]);
+ bcopy(as, eaddr, sizeof(eaddr));
+ }
if (sc->rl_type == RL_8169) {
- /* Set RX length mask */
+ /* Set RX length mask and number of descriptors. */
sc->rl_rxlenmask = RL_RDESC_STAT_GFRAGLEN;
sc->rl_txstart = RL_GTXSTART;
+ sc->rl_ldata.rl_tx_desc_cnt = RL_8169_TX_DESC_CNT;
+ sc->rl_ldata.rl_rx_desc_cnt = RL_8169_RX_DESC_CNT;
} else {
- /* Set RX length mask */
+ /* Set RX length mask and number of descriptors. */
sc->rl_rxlenmask = RL_RDESC_STAT_FRAGLEN;
sc->rl_txstart = RL_TXSTART;
+ sc->rl_ldata.rl_tx_desc_cnt = RL_8139_TX_DESC_CNT;
+ sc->rl_ldata.rl_rx_desc_cnt = RL_8139_RX_DESC_CNT;
}
- /*
- * Allocate the parent bus DMA tag appropriate for PCI.
- */
-#define RL_NSEG_NEW 32
- error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
- BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
- MAXBSIZE, RL_NSEG_NEW, BUS_SPACE_MAXSIZE_32BIT, 0,
- NULL, NULL, &sc->rl_parent_tag);
- if (error)
- goto fail;
-
error = re_allocmem(dev, sc);
-
if (error)
goto fail;
@@ -1353,6 +1503,22 @@ re_attach(dev)
goto fail;
}
+ /* Take controller out of deep sleep mode. */
+ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
+ if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
+ CSR_WRITE_1(sc, RL_GPIO,
+ CSR_READ_1(sc, RL_GPIO) | 0x01);
+ else
+ CSR_WRITE_1(sc, RL_GPIO,
+ CSR_READ_1(sc, RL_GPIO) & ~0x01);
+ }
+
+ /* Take PHY out of power down mode. */
+ if ((sc->rl_flags & RL_FLAG_PHYWAKE) != 0) {
+ re_gmii_writereg(dev, 1, 0x1f, 0);
+ re_gmii_writereg(dev, 1, 0x0e, 0);
+ }
+
/* Do MII setup */
#ifndef __rtems__
if (mii_phy_probe(dev, &sc->rl_miibus,
@@ -1363,29 +1529,6 @@ re_attach(dev)
}
#endif
- /* Take PHY out of power down mode. */
- if (sc->rl_type == RL_8169) {
- uint32_t rev;
-
- rev = CSR_READ_4(sc, RL_TXCFG);
- /* HWVERID 0, 1 and 2 : bit26-30, bit23 */
- rev &= 0x7c800000;
- if (rev != 0) {
- /* RTL8169S single chip */
- switch (rev) {
- case RL_HWREV_8169_8110SB:
- case RL_HWREV_8169_8110SC:
- case RL_HWREV_8168_SPIN2:
- case RL_HWREV_8168_SPIN3:
- re_gmii_writereg(dev, 1, 0x1f, 0);
- re_gmii_writereg(dev, 1, 0x0e, 0);
- break;
- default:
- break;
- }
- }
- }
-
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -1404,6 +1547,25 @@ re_attach(dev)
TASK_INIT(&sc->rl_txtask, 1, re_tx_task, ifp);
TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc);
+#ifdef __rtems__
+ taskqueue_create_fast("re_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &taskqueue_fast);
+ taskqueue_start_threads(&taskqueue_fast, 1, PI_NET, "%s taskq",
+ device_get_nameunit(dev));
+#endif
+
+ /*
+ * XXX
+ * Still have no idea how to make TSO work on 8168C, 8168CP,
+ * 8111C and 8111CP.
+ */
+#ifndef __rtems__
+ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
+ ifp->if_hwassist |= CSUM_TSO;
+ ifp->if_capabilities |= IFCAP_TSO4;
+ }
+#endif
+
/*
* Call MI attach routine.
*/
@@ -1412,11 +1574,19 @@ re_attach(dev)
#ifndef __rtems__
/* VLAN capability setup */
ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING;
-#ifdef IFCAP_VLAN_HWCSUM
if (ifp->if_capabilities & IFCAP_HWCSUM)
ifp->if_capabilities |= IFCAP_VLAN_HWCSUM;
-#endif
+ /* Enable WOL if PM is supported. */
+ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &reg) == 0)
+ ifp->if_capabilities |= IFCAP_WOL;
ifp->if_capenable = ifp->if_capabilities;
+ /*
+ * Don't enable TSO by default. Under certain
+ * circumtances the controller generated corrupted
+ * packets in TSO size.
+ */
+ ifp->if_hwassist &= ~CSUM_TSO;
+ ifp->if_capenable &= ~IFCAP_TSO4;
#ifdef DEVICE_POLLING
ifp->if_capabilities |= IFCAP_POLLING;
#endif
@@ -1427,7 +1597,7 @@ re_attach(dev)
*/
ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
#endif
-
+
#ifdef RE_DIAG
/*
* Perform hardware diagnostic on the original RTL8169.
@@ -1447,8 +1617,19 @@ re_attach(dev)
#endif
/* Hook interrupt last to avoid having to lock softc */
- error = bus_setup_intr(dev, sc->rl_irq, INTR_TYPE_NET | INTR_MPSAFE |
- INTR_FAST, NULL, re_intr, sc, &sc->rl_intrhand);
+ if ((sc->rl_flags & RL_FLAG_MSI) == 0)
+ error = bus_setup_intr(dev, sc->rl_irq[0],
+ INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc,
+ &sc->rl_intrhand[0]);
+ else {
+ for (i = 0; i < RL_MSI_MESSAGES; i++) {
+ error = bus_setup_intr(dev, sc->rl_irq[i],
+ INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc,
+ &sc->rl_intrhand[i]);
+ if (error != 0)
+ break;
+ }
+ }
if (error) {
device_printf(dev, "couldn't set up irq\n");
ether_ifdetach(ifp);
@@ -1470,23 +1651,22 @@ fail:
* allocated.
*/
static int
-re_detach(dev)
- device_t dev;
+re_detach(device_t dev)
{
struct rl_softc *sc;
struct ifnet *ifp;
- int i;
+ int i, rid;
sc = device_get_softc(dev);
ifp = sc->rl_ifp;
KASSERT(mtx_initialized(&sc->rl_mtx), ("re 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)) {
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING)
+ ether_poll_deregister(ifp);
+#endif
RL_LOCK(sc);
#if 0
sc->suspended = 1;
@@ -1520,14 +1700,34 @@ re_detach(dev)
* stopped here.
*/
- if (sc->rl_intrhand)
- bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand);
+ for (i = 0; i < RL_MSI_MESSAGES; i++) {
+ if (sc->rl_intrhand[i] != NULL) {
+ bus_teardown_intr(dev, sc->rl_irq[i],
+ sc->rl_intrhand[i]);
+ sc->rl_intrhand[i] = NULL;
+ }
+ }
if (ifp != NULL)
if_free(ifp);
- if (sc->rl_irq)
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq);
+ if ((sc->rl_flags & RL_FLAG_MSI) == 0) {
+ if (sc->rl_irq[0] != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->rl_irq[0]);
+ sc->rl_irq[0] = NULL;
+ }
+ } else {
+ for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) {
+ if (sc->rl_irq[i] != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid,
+ sc->rl_irq[i]);
+ sc->rl_irq[i] = NULL;
+ }
+ }
+ pci_release_msi(dev);
+ }
if (sc->rl_res)
- bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res);
+ bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id,
+ sc->rl_res);
/* Unload and free the RX DMA ring memory and map */
@@ -1553,14 +1753,20 @@ re_detach(dev)
/* Destroy all the RX and TX buffer maps */
- if (sc->rl_ldata.rl_mtag) {
- for (i = 0; i < RL_TX_DESC_CNT; i++)
- bus_dmamap_destroy(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_tx_dmamap[i]);
- for (i = 0; i < RL_RX_DESC_CNT; i++)
- bus_dmamap_destroy(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[i]);
- bus_dma_tag_destroy(sc->rl_ldata.rl_mtag);
+ if (sc->rl_ldata.rl_tx_mtag) {
+ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++)
+ bus_dmamap_destroy(sc->rl_ldata.rl_tx_mtag,
+ sc->rl_ldata.rl_tx_desc[i].tx_dmamap);
+ bus_dma_tag_destroy(sc->rl_ldata.rl_tx_mtag);
+ }
+ if (sc->rl_ldata.rl_rx_mtag) {
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++)
+ bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag,
+ sc->rl_ldata.rl_rx_desc[i].rx_dmamap);
+ if (sc->rl_ldata.rl_rx_sparemap)
+ bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag,
+ sc->rl_ldata.rl_rx_sparemap);
+ bus_dma_tag_destroy(sc->rl_ldata.rl_rx_mtag);
}
/* Unload and free the stats buffer and map */
@@ -1582,23 +1788,36 @@ re_detach(dev)
return (0);
}
+static __inline void
+re_discard_rxbuf(struct rl_softc *sc, int idx)
+{
+ struct rl_desc *desc;
+ struct rl_rxdesc *rxd;
+ uint32_t cmdstat;
+
+ rxd = &sc->rl_ldata.rl_rx_desc[idx];
+ desc = &sc->rl_ldata.rl_rx_list[idx];
+ desc->rl_vlanctl = 0;
+ cmdstat = rxd->rx_size;
+ if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1)
+ cmdstat |= RL_RDESC_CMD_EOR;
+ desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN);
+}
+
static int
-re_newbuf(sc, idx, m)
- struct rl_softc *sc;
- int idx;
- struct mbuf *m;
+re_newbuf(struct rl_softc *sc, int idx)
{
- struct rl_dmaload_arg arg;
- struct mbuf *n = NULL;
- int error;
+ struct mbuf *m;
+ struct rl_rxdesc *rxd;
+ bus_dma_segment_t segs[1];
+ bus_dmamap_t map;
+ struct rl_desc *desc;
+ uint32_t cmdstat;
+ int error, nsegs;
- if (m == NULL) {
- n = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
- if (n == NULL)
- return (ENOBUFS);
- m = n;
- } else
- m->m_data = m->m_ext.ext_buf;
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (ENOBUFS);
m->m_len = m->m_pkthdr.len = MCLBYTES;
#ifdef RE_FIXUP_RX
@@ -1614,37 +1833,44 @@ re_newbuf(sc, idx, m)
*/
m_adj(m, RE_ETHER_ALIGN);
#endif
- arg.rl_idx = idx;
- arg.rl_maxsegs = 1;
- arg.rl_flags = 0;
- arg.rl_ring = sc->rl_ldata.rl_rx_list;
-
- error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[idx], m, re_dma_map_desc,
- &arg, BUS_DMA_NOWAIT);
- if (error || arg.rl_maxsegs != 1) {
- if (n != NULL)
- m_freem(n);
- if (arg.rl_maxsegs == 0)
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[idx]);
- return (ENOMEM);
+ error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_rx_mtag,
+ sc->rl_ldata.rl_rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_freem(m);
+ return (ENOBUFS);
}
+ KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs));
- sc->rl_ldata.rl_rx_list[idx].rl_cmdstat |= htole32(RL_RDESC_CMD_OWN);
- sc->rl_ldata.rl_rx_mbuf[idx] = m;
+ rxd = &sc->rl_ldata.rl_rx_desc[idx];
+ if (rxd->rx_m != NULL) {
+ bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap);
+ }
- bus_dmamap_sync(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[idx],
+ rxd->rx_m = m;
+ map = rxd->rx_dmamap;
+ rxd->rx_dmamap = sc->rl_ldata.rl_rx_sparemap;
+ rxd->rx_size = segs[0].ds_len;
+ sc->rl_ldata.rl_rx_sparemap = map;
+ bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap,
BUS_DMASYNC_PREREAD);
+ desc = &sc->rl_ldata.rl_rx_list[idx];
+ desc->rl_vlanctl = 0;
+ desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[0].ds_addr));
+ desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[0].ds_addr));
+ cmdstat = segs[0].ds_len;
+ if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1)
+ cmdstat |= RL_RDESC_CMD_EOR;
+ desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN);
+
return (0);
}
#ifdef RE_FIXUP_RX
static __inline void
-re_fixup_rx(m)
- struct mbuf *m;
+re_fixup_rx(struct mbuf *m)
{
int i;
uint16_t *src, *dst;
@@ -1656,44 +1882,47 @@ re_fixup_rx(m)
*dst++ = *src++;
m->m_data -= RE_ETHER_ALIGN - ETHER_ALIGN;
-
- return;
}
#endif
static int
-re_tx_list_init(sc)
- struct rl_softc *sc;
+re_tx_list_init(struct rl_softc *sc)
{
+ struct rl_desc *desc;
+ int i;
RL_LOCK_ASSERT(sc);
- bzero ((char *)sc->rl_ldata.rl_tx_list, RL_TX_LIST_SZ);
- bzero ((char *)&sc->rl_ldata.rl_tx_mbuf,
- (RL_TX_DESC_CNT * sizeof(struct mbuf *)));
+ bzero(sc->rl_ldata.rl_tx_list,
+ sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc));
+ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++)
+ sc->rl_ldata.rl_tx_desc[i].tx_m = NULL;
+ /* Set EOR. */
+ desc = &sc->rl_ldata.rl_tx_list[sc->rl_ldata.rl_tx_desc_cnt - 1];
+ desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOR);
bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
- sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREWRITE);
+ sc->rl_ldata.rl_tx_list_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
sc->rl_ldata.rl_tx_prodidx = 0;
sc->rl_ldata.rl_tx_considx = 0;
- sc->rl_ldata.rl_tx_free = RL_TX_DESC_CNT;
+ sc->rl_ldata.rl_tx_free = sc->rl_ldata.rl_tx_desc_cnt;
return (0);
}
static int
-re_rx_list_init(sc)
- struct rl_softc *sc;
+re_rx_list_init(struct rl_softc *sc)
{
- int i;
-
- bzero ((char *)sc->rl_ldata.rl_rx_list, RL_RX_LIST_SZ);
- bzero ((char *)&sc->rl_ldata.rl_rx_mbuf,
- (RL_RX_DESC_CNT * sizeof(struct mbuf *)));
+ int error, i;
- for (i = 0; i < RL_RX_DESC_CNT; i++) {
- if (re_newbuf(sc, i, NULL) == ENOBUFS)
- return (ENOBUFS);
+ bzero(sc->rl_ldata.rl_rx_list,
+ sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc));
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ sc->rl_ldata.rl_rx_desc[i].rx_m = NULL;
+ if ((error = re_newbuf(sc, i)) != 0)
+ return (error);
}
/* Flush the RX descriptors */
@@ -1714,43 +1943,48 @@ re_rx_list_init(sc)
* across multiple 2K mbuf cluster buffers.
*/
static int
-re_rxeof(sc)
- struct rl_softc *sc;
+re_rxeof(struct rl_softc *sc, int *rx_npktsp)
{
struct mbuf *m;
struct ifnet *ifp;
int i, total_len;
struct rl_desc *cur_rx;
u_int32_t rxstat, rxvlan;
- int maxpkt = 16;
+ int maxpkt = 16, rx_npkts = 0;
RL_LOCK_ASSERT(sc);
ifp = sc->rl_ifp;
- i = sc->rl_ldata.rl_rx_prodidx;
/* Invalidate the descriptor memory */
bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
sc->rl_ldata.rl_rx_list_map,
- BUS_DMASYNC_POSTREAD);
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- while (!RL_OWN(&sc->rl_ldata.rl_rx_list[i]) && maxpkt) {
+ for (i = sc->rl_ldata.rl_rx_prodidx; maxpkt > 0;
+ i = RL_RX_DESC_NXT(sc, i)) {
cur_rx = &sc->rl_ldata.rl_rx_list[i];
- m = sc->rl_ldata.rl_rx_mbuf[i];
- total_len = RL_RXBYTES(cur_rx);
rxstat = le32toh(cur_rx->rl_cmdstat);
+ if ((rxstat & RL_RDESC_STAT_OWN) != 0)
+ break;
+ total_len = rxstat & sc->rl_rxlenmask;
rxvlan = le32toh(cur_rx->rl_vlanctl);
-
- /* Invalidate the RX mbuf and unload its map */
-
- bus_dmamap_sync(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[i],
- BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[i]);
+ m = sc->rl_ldata.rl_rx_desc[i].rx_m;
if (!(rxstat & RL_RDESC_STAT_EOF)) {
+ if (re_newbuf(sc, i) != 0) {
+ /*
+ * If this is part of a multi-fragment packet,
+ * discard all the pieces.
+ */
+ if (sc->rl_head != NULL) {
+ m_freem(sc->rl_head);
+ sc->rl_head = sc->rl_tail = NULL;
+ }
+ re_discard_rxbuf(sc, i);
+ continue;
+ }
m->m_len = RE_RX_DESC_BUFLEN;
if (sc->rl_head == NULL)
sc->rl_head = sc->rl_tail = m;
@@ -1759,8 +1993,6 @@ re_rxeof(sc)
sc->rl_tail->m_next = m;
sc->rl_tail = m;
}
- re_newbuf(sc, i, NULL);
- RL_DESC_INC(i);
continue;
}
@@ -1798,8 +2030,7 @@ re_rxeof(sc)
m_freem(sc->rl_head);
sc->rl_head = sc->rl_tail = NULL;
}
- re_newbuf(sc, i, m);
- RL_DESC_INC(i);
+ re_discard_rxbuf(sc, i);
continue;
}
@@ -1808,19 +2039,16 @@ re_rxeof(sc)
* reload the current one.
*/
- if (re_newbuf(sc, i, NULL)) {
- ifp->if_ierrors++;
+ if (re_newbuf(sc, i) != 0) {
+ ifp->if_iqdrops++;
if (sc->rl_head != NULL) {
m_freem(sc->rl_head);
sc->rl_head = sc->rl_tail = NULL;
}
- re_newbuf(sc, i, m);
- RL_DESC_INC(i);
+ re_discard_rxbuf(sc, i);
continue;
}
- RL_DESC_INC(i);
-
if (sc->rl_head != NULL) {
m->m_len = total_len % RE_RX_DESC_BUFLEN;
if (m->m_len == 0)
@@ -1857,31 +2085,53 @@ re_rxeof(sc)
#ifndef __rtems__
if (ifp->if_capenable & IFCAP_RXCSUM) {
-
- /* Check IP header checksum */
- if (rxstat & RL_RDESC_STAT_PROTOID)
- m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
- if (!(rxstat & RL_RDESC_STAT_IPSUMBAD))
- m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
-
- /* Check TCP/UDP checksum */
- if ((RL_TCPPKT(rxstat) &&
- !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) ||
- (RL_UDPPKT(rxstat) &&
- !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) {
- m->m_pkthdr.csum_flags |=
- CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
- m->m_pkthdr.csum_data = 0xffff;
+ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
+ /* Check IP header checksum */
+ if (rxstat & RL_RDESC_STAT_PROTOID)
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED;
+ if (!(rxstat & RL_RDESC_STAT_IPSUMBAD))
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_VALID;
+
+ /* Check TCP/UDP checksum */
+ if ((RL_TCPPKT(rxstat) &&
+ !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) ||
+ (RL_UDPPKT(rxstat) &&
+ !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ } else {
+ /*
+ * RTL8168C/RTL816CP/RTL8111C/RTL8111CP
+ */
+ if ((rxstat & RL_RDESC_STAT_PROTOID) &&
+ (rxvlan & RL_RDESC_IPV4))
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED;
+ if (!(rxstat & RL_RDESC_STAT_IPSUMBAD) &&
+ (rxvlan & RL_RDESC_IPV4))
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_VALID;
+ if (((rxstat & RL_RDESC_STAT_TCP) &&
+ !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) ||
+ ((rxstat & RL_RDESC_STAT_UDP) &&
+ !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
}
}
#endif
maxpkt--;
#ifndef __rtems__
if (rxvlan & RL_RDESC_VLANCTL_TAG) {
- VLAN_INPUT_TAG_NEW(ifp, m,
- ntohs((rxvlan & RL_RDESC_VLANCTL_DATA)));
- if (m == NULL)
- continue;
+ m->m_pkthdr.ether_vtag =
+ bswap16((rxvlan & RL_RDESC_VLANCTL_DATA));
+ m->m_flags |= M_VLANTAG;
}
#endif
RL_UNLOCK(sc);
@@ -1891,6 +2141,7 @@ re_rxeof(sc)
ether_input_skipping(ifp, m);
#endif
RL_LOCK(sc);
+ rx_npkts++;
}
/* Flush the RX DMA ring */
@@ -1901,6 +2152,8 @@ re_rxeof(sc)
sc->rl_ldata.rl_rx_prodidx = i;
+ if (rx_npktsp != NULL)
+ *rx_npktsp = rx_npkts;
if (maxpkt)
return(EAGAIN);
@@ -1908,28 +2161,28 @@ re_rxeof(sc)
}
static void
-re_txeof(sc)
- struct rl_softc *sc;
+re_txeof(struct rl_softc *sc)
{
struct ifnet *ifp;
+ struct rl_txdesc *txd;
u_int32_t txstat;
- int idx;
+ int cons;
- ifp = sc->rl_ifp;
- idx = sc->rl_ldata.rl_tx_considx;
+ cons = sc->rl_ldata.rl_tx_considx;
+ if (cons == sc->rl_ldata.rl_tx_prodidx)
+ return;
+ ifp = sc->rl_ifp;
/* Invalidate the TX descriptor list */
bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
sc->rl_ldata.rl_tx_list_map,
- BUS_DMASYNC_POSTREAD);
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- while (sc->rl_ldata.rl_tx_free < RL_TX_DESC_CNT) {
- txstat = le32toh(sc->rl_ldata.rl_tx_list[idx].rl_cmdstat);
- if (txstat & RL_TDESC_CMD_OWN)
+ for (; cons != sc->rl_ldata.rl_tx_prodidx;
+ cons = RL_TX_DESC_NXT(sc, cons)) {
+ txstat = le32toh(sc->rl_ldata.rl_tx_list[cons].rl_cmdstat);
+ if (txstat & RL_TDESC_STAT_OWN)
break;
-
- sc->rl_ldata.rl_tx_list[idx].rl_bufaddr_lo = 0;
-
/*
* We only stash mbufs in the last descriptor
* in a fragment chain, which also happens to
@@ -1937,10 +2190,15 @@ re_txeof(sc)
* are valid.
*/
if (txstat & RL_TDESC_CMD_EOF) {
- m_freem(sc->rl_ldata.rl_tx_mbuf[idx]);
- sc->rl_ldata.rl_tx_mbuf[idx] = NULL;
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_tx_dmamap[idx]);
+ txd = &sc->rl_ldata.rl_tx_desc[cons];
+ bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag,
+ txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag,
+ txd->tx_dmamap);
+ KASSERT(txd->tx_m != NULL,
+ ("%s: freeing NULL mbufs!", __func__));
+ m_freem(txd->tx_m);
+ txd->tx_m = NULL;
if (txstat & (RL_TDESC_STAT_EXCESSCOL|
RL_TDESC_STAT_COLCNT))
ifp->if_collisions++;
@@ -1950,26 +2208,13 @@ re_txeof(sc)
ifp->if_opackets++;
}
sc->rl_ldata.rl_tx_free++;
- RL_DESC_INC(idx);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
}
- sc->rl_ldata.rl_tx_considx = idx;
+ sc->rl_ldata.rl_tx_considx = cons;
/* No changes made to the TX ring, so no flush needed */
- if (sc->rl_ldata.rl_tx_free > RL_TX_DESC_THLD)
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-
- if (sc->rl_ldata.rl_tx_free < RL_TX_DESC_CNT) {
- /*
- * Some chips will ignore a second TX request issued
- * while an existing transmission is in progress. If
- * the transmitter goes idle but there are still
- * packets waiting to be sent, we need to restart the
- * channel here to flush them out. This only seems to
- * be required with the PCIe devices.
- */
- CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
-
+ if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) {
#ifdef RE_TX_MODERATION
/*
* If not all descriptors have been reaped yet, reload
@@ -1984,61 +2229,84 @@ re_txeof(sc)
}
static void
-re_tick(xsc)
- void *xsc;
+re_tick(void *xsc)
{
struct rl_softc *sc;
struct mii_data *mii;
- struct ifnet *ifp;
sc = xsc;
- ifp = sc->rl_ifp;
RL_LOCK_ASSERT(sc);
- re_watchdog(sc);
-
mii = device_get_softc(sc->rl_miibus);
#ifndef __rtems__
mii_tick(mii);
- if (sc->rl_link) {
- if (!(mii->mii_media_status & IFM_ACTIVE))
- sc->rl_link = 0;
- } else {
- if (mii->mii_media_status & IFM_ACTIVE &&
- IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
- sc->rl_link = 1;
- if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->rl_txtask);
- }
+ if ((sc->rl_flags & RL_FLAG_LINK) == 0)
+ re_miibus_statchg(sc->rl_dev);
+#else
+ {
+ struct ifnet *ifp;
+ int med, err;
+
+ ifp = sc->rl_ifp;
+
+ med = IFM_MAKEWORD(0,0,0,0);
+
+ if ( (err = rtems_mii_ioctl( RE_MDIO(sc), sc, SIOCGIFMEDIA, &med)) ) {
+ device_printf(sc->rl_dev, "WARNING: mii ioctl failed; unable to determine link status -- fake ON\n");
+ med = IFM_LINK_OK;
+ }
+
+ /* link just died */
+ if ( (sc->rl_flags & RL_FLAG_LINK) & !(IFM_LINK_OK & med) ) {
+ sc->rl_flags &= ~RL_FLAG_LINK;
}
+ /* link just came up, restart */
+ if ( ((sc->rl_flags & RL_FLAG_LINK) == 0) && (IFM_LINK_OK & med) ) {
+ sc->rl_flags |= RL_FLAG_LINK;
+ if ( ifp->if_snd.ifq_head != NULL ) {
+ taskqueue_enqueue_fast(taskqueue_fast,
+ &sc->rl_txtask);
+ }
+ }
+ }
#endif
+ /*
+ * Reclaim transmitted frames here. Technically it is not
+ * necessary to do here but it ensures periodic reclamation
+ * regardless of Tx completion interrupt which seems to be
+ * lost on PCIe based controllers under certain situations.
+ */
+ re_txeof(sc);
+ re_watchdog(sc);
callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
}
#ifdef DEVICE_POLLING
-static void
+static int
re_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)
- re_poll_locked(ifp, cmd, count);
+ rx_npkts = re_poll_locked(ifp, cmd, count);
RL_UNLOCK(sc);
+ return (rx_npkts);
}
-static void
+static int
re_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;
- re_rxeof(sc);
+ re_rxeof(sc, &rx_npkts);
re_txeof(sc);
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
@@ -2049,46 +2317,44 @@ re_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
status = CSR_READ_2(sc, RL_ISR);
if (status == 0xffff)
- return;
+ return (rx_npkts);
if (status)
CSR_WRITE_2(sc, RL_ISR, status);
+ if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) &&
+ (sc->rl_flags & RL_FLAG_PCIE))
+ CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
/*
* XXX check behaviour on receiver stalls.
*/
- if (status & RL_ISR_SYSTEM_ERR) {
- re_reset(sc);
+ if (status & RL_ISR_SYSTEM_ERR)
re_init_locked(sc);
- }
}
+ return (rx_npkts);
}
#endif /* DEVICE_POLLING */
-static void
-re_intr(arg)
- void *arg;
+static int
+re_intr(void *arg)
{
struct rl_softc *sc;
uint16_t status;
sc = arg;
-printk( "re_intr " );
status = CSR_READ_2(sc, RL_ISR);
if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0)
- return;
+ return (FILTER_STRAY);
CSR_WRITE_2(sc, RL_IMR, 0);
taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask);
- return;
+ return (FILTER_HANDLED);
}
static void
-re_int_task(arg, npending)
- void *arg;
- int npending;
+re_int_task(void *arg, int npending)
{
struct rl_softc *sc;
struct ifnet *ifp;
@@ -2098,52 +2364,54 @@ re_int_task(arg, npending)
sc = arg;
ifp = sc->rl_ifp;
- NET_LOCK_GIANT();
RL_LOCK(sc);
status = CSR_READ_2(sc, RL_ISR);
CSR_WRITE_2(sc, RL_ISR, status);
- if (sc->suspended || !(ifp->if_flags & IFF_UP)) {
+ if (sc->suspended ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
RL_UNLOCK(sc);
- NET_UNLOCK_GIANT();
return;
}
#ifdef DEVICE_POLLING
if (ifp->if_capenable & IFCAP_POLLING) {
RL_UNLOCK(sc);
- NET_UNLOCK_GIANT();
return;
}
#endif
if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW))
- rval = re_rxeof(sc);
+ rval = re_rxeof(sc, NULL);
+ /*
+ * Some chips will ignore a second TX request issued
+ * while an existing transmission is in progress. If
+ * the transmitter goes idle but there are still
+ * packets waiting to be sent, we need to restart the
+ * channel here to flush them out. This only seems to
+ * be required with the PCIe devices.
+ */
+ if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) &&
+ (sc->rl_flags & RL_FLAG_PCIE))
+ CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
+ if (status & (
#ifdef RE_TX_MODERATION
- if (status & (RL_ISR_TIMEOUT_EXPIRED|
+ RL_ISR_TIMEOUT_EXPIRED|
#else
- if (status & (RL_ISR_TX_OK|
+ RL_ISR_TX_OK|
#endif
RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL))
re_txeof(sc);
- if (status & RL_ISR_SYSTEM_ERR) {
- re_reset(sc);
+ if (status & RL_ISR_SYSTEM_ERR)
re_init_locked(sc);
- }
-
- if (status & RL_ISR_LINKCHG) {
- callout_stop(&sc->rl_stat_callout);
- re_tick(sc);
- }
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
RL_UNLOCK(sc);
- NET_UNLOCK_GIANT();
if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) {
taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask);
@@ -2151,54 +2419,25 @@ re_int_task(arg, npending)
}
CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS);
-
- return;
}
static int
-re_encap(sc, m_head, idx)
- struct rl_softc *sc;
- struct mbuf **m_head;
- int *idx;
+re_encap(struct rl_softc *sc, struct mbuf **m_head)
{
- struct mbuf *m_new = NULL;
- struct rl_dmaload_arg arg;
+ struct rl_txdesc *txd, *txd_last;
+ bus_dma_segment_t segs[RL_NTXSEGS];
bus_dmamap_t map;
- int error;
+ struct mbuf *m_new;
+ struct rl_desc *desc;
+ int nsegs, prod;
+ int i, error, ei, si;
#ifndef __rtems__
- struct m_tag *mtag;
+ int padlen;
#endif
-
+ uint32_t cmdstat, csum_flags, vlanctl;
+
RL_LOCK_ASSERT(sc);
-
- if (sc->rl_ldata.rl_tx_free <= RL_TX_DESC_THLD)
- return (EFBIG);
-
- /*
- * Set up checksum offload. Note: checksum offload bits must
- * appear in all descriptors of a multi-descriptor transmit
- * attempt. This is according to testing done with an 8169
- * chip. This is a requirement.
- */
-
- arg.rl_flags = 0;
-
-#ifndef __rtems__
- if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP)
- arg.rl_flags |= RL_TDESC_CMD_IPCSUM;
- if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
- arg.rl_flags |= RL_TDESC_CMD_TCPCSUM;
- if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
- arg.rl_flags |= RL_TDESC_CMD_UDPCSUM;
-#endif
-
- arg.rl_idx = *idx;
- arg.rl_maxsegs = sc->rl_ldata.rl_tx_free;
- if (arg.rl_maxsegs > RL_TX_DESC_THLD)
- arg.rl_maxsegs -= RL_TX_DESC_THLD;
- arg.rl_ring = sc->rl_ldata.rl_tx_list;
-
- map = sc->rl_ldata.rl_tx_dmamap[*idx];
+ M_ASSERTPKTHDR((*m_head));
/*
* With some of the RealTek chips, using the checksum offload
@@ -2207,151 +2446,215 @@ re_encap(sc, m_head, idx)
* need to send a really small IP fragment that's less than 60
* bytes in size, and IP header checksumming is enabled, the
* resulting ethernet frame that appears on the wire will
- * have garbled payload. To work around this, if TX checksum
+ * have garbled payload. To work around this, if TX IP checksum
* offload is enabled, we always manually pad short frames out
- * to the minimum ethernet frame size. We do this by pretending
- * the mbuf chain has too many fragments so the coalescing code
- * below can assemble the packet into a single buffer that's
- * padded out to the mininum frame size.
- *
- * Note: this appears unnecessary for TCP, and doing it for TCP
- * with PCIe adapters seems to result in bad checksums.
+ * to the minimum ethernet frame size.
*/
-
- if (arg.rl_flags && !(arg.rl_flags & RL_TDESC_CMD_TCPCSUM) &&
- (*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN)
- error = EFBIG;
- else
- error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, map,
- *m_head, re_dma_map_desc, &arg, BUS_DMA_NOWAIT);
-
- if (error && error != EFBIG) {
- device_printf(sc->rl_dev, "can't map mbuf (error %d)\n", error);
- return (ENOBUFS);
- }
-
- /* Too many segments to map, coalesce into a single mbuf */
-
- if (error || arg.rl_maxsegs == 0) {
- if (arg.rl_maxsegs == 0)
- bus_dmamap_unload(sc->rl_ldata.rl_mtag, map);
- m_new = m_defrag(*m_head, M_DONTWAIT);
- if (m_new == NULL) {
+#ifndef __rtems__
+ if ((sc->rl_flags & RL_FLAG_AUTOPAD) == 0 &&
+ (*m_head)->m_pkthdr.len < RL_IP4CSUMTX_PADLEN &&
+ ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) != 0) {
+ padlen = RL_MIN_FRAMELEN - (*m_head)->m_pkthdr.len;
+ if (M_WRITABLE(*m_head) == 0) {
+ /* Get a writable copy. */
+ m_new = m_dup(*m_head, M_DONTWAIT);
m_freem(*m_head);
- *m_head = NULL;
- return (ENOBUFS);
+ if (m_new == NULL) {
+ *m_head = NULL;
+ return (ENOBUFS);
+ }
+ *m_head = m_new;
}
- *m_head = m_new;
+ if ((*m_head)->m_next != NULL ||
+ M_TRAILINGSPACE(*m_head) < padlen) {
+ m_new = m_defrag(*m_head, M_DONTWAIT);
+ if (m_new == NULL) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (ENOBUFS);
+ }
+ } else
+ m_new = *m_head;
/*
* Manually pad short frames, and zero the pad space
* to avoid leaking data.
*/
- if (m_new->m_pkthdr.len < RL_MIN_FRAMELEN) {
- bzero(mtod(m_new, char *) + m_new->m_pkthdr.len,
- RL_MIN_FRAMELEN - m_new->m_pkthdr.len);
- m_new->m_pkthdr.len += RL_MIN_FRAMELEN -
- m_new->m_pkthdr.len;
- m_new->m_len = m_new->m_pkthdr.len;
+ bzero(mtod(m_new, char *) + m_new->m_pkthdr.len, padlen);
+ m_new->m_pkthdr.len += padlen;
+ m_new->m_len = m_new->m_pkthdr.len;
+ *m_head = m_new;
+ }
+#endif
+
+ prod = sc->rl_ldata.rl_tx_prodidx;
+ txd = &sc->rl_ldata.rl_tx_desc[prod];
+ error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap,
+ *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error == EFBIG) {
+#ifndef __rtems__
+ m_new = m_collapse(*m_head, M_DONTWAIT, RL_NTXSEGS);
+#else
+ m_new = m_defrag(*m_head, M_DONTWAIT);
+#endif
+ if (m_new == NULL) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (ENOBUFS);
}
-
- /* Note that we'll run over RL_TX_DESC_THLD here. */
- arg.rl_maxsegs = sc->rl_ldata.rl_tx_free;
- error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, map,
- *m_head, re_dma_map_desc, &arg, BUS_DMA_NOWAIT);
- if (error || arg.rl_maxsegs == 0) {
- device_printf(sc->rl_dev,
- "can't map defragmented mbuf (error %d)\n", error);
- m_freem(m_new);
+ *m_head = m_new;
+ error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag,
+ txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_freem(*m_head);
*m_head = NULL;
- if (arg.rl_maxsegs == 0)
- bus_dmamap_unload(sc->rl_ldata.rl_mtag, map);
- return (EFBIG);
+ return (error);
}
+ } else if (error != 0)
+ return (error);
+ if (nsegs == 0) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (EIO);
}
+ /* Check for number of available descriptors. */
+ if (sc->rl_ldata.rl_tx_free - nsegs <= 1) {
+ bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap);
+ return (ENOBUFS);
+ }
+
+ bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
/*
- * Insure that the map for this transmission
- * is placed at the array index of the last descriptor
- * in this chain. (Swap last and first dmamaps.)
+ * Set up checksum offload. Note: checksum offload bits must
+ * appear in all descriptors of a multi-descriptor transmit
+ * attempt. This is according to testing done with an 8169
+ * chip. This is a requirement.
*/
- sc->rl_ldata.rl_tx_dmamap[*idx] =
- sc->rl_ldata.rl_tx_dmamap[arg.rl_idx];
- sc->rl_ldata.rl_tx_dmamap[arg.rl_idx] = map;
-
- sc->rl_ldata.rl_tx_mbuf[arg.rl_idx] = *m_head;
- sc->rl_ldata.rl_tx_free -= arg.rl_maxsegs;
+ vlanctl = 0;
+ csum_flags = 0;
+#ifndef __rtems__
+ if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0)
+ csum_flags = RL_TDESC_CMD_LGSEND |
+ ((uint32_t)(*m_head)->m_pkthdr.tso_segsz <<
+ RL_TDESC_CMD_MSSVAL_SHIFT);
+ else {
+ /*
+ * Unconditionally enable IP checksum if TCP or UDP
+ * checksum is required. Otherwise, TCP/UDP checksum
+ * does't make effects.
+ */
+ if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) {
+ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
+ csum_flags |= RL_TDESC_CMD_IPCSUM;
+ if (((*m_head)->m_pkthdr.csum_flags &
+ CSUM_TCP) != 0)
+ csum_flags |= RL_TDESC_CMD_TCPCSUM;
+ if (((*m_head)->m_pkthdr.csum_flags &
+ CSUM_UDP) != 0)
+ csum_flags |= RL_TDESC_CMD_UDPCSUM;
+ } else {
+ vlanctl |= RL_TDESC_CMD_IPCSUMV2;
+ if (((*m_head)->m_pkthdr.csum_flags &
+ CSUM_TCP) != 0)
+ vlanctl |= RL_TDESC_CMD_TCPCSUMV2;
+ if (((*m_head)->m_pkthdr.csum_flags &
+ CSUM_UDP) != 0)
+ vlanctl |= RL_TDESC_CMD_UDPCSUMV2;
+ }
+ }
+ }
+#endif
/*
* Set up hardware VLAN tagging. Note: vlan tag info must
- * appear in the first descriptor of a multi-descriptor
+ * appear in all descriptors of a multi-descriptor
* transmission attempt.
*/
-
#ifndef __rtems__
- mtag = VLAN_OUTPUT_TAG(sc->rl_ifp, *m_head);
- if (mtag != NULL)
- sc->rl_ldata.rl_tx_list[*idx].rl_vlanctl =
- htole32(htons(VLAN_TAG_VALUE(mtag)) | RL_TDESC_VLANCTL_TAG);
+ if ((*m_head)->m_flags & M_VLANTAG)
+ vlanctl |= bswap16((*m_head)->m_pkthdr.ether_vtag) |
+ RL_TDESC_VLANCTL_TAG;
#endif
+
+ si = prod;
+ for (i = 0; i < nsegs; i++, prod = RL_TX_DESC_NXT(sc, prod)) {
+ desc = &sc->rl_ldata.rl_tx_list[prod];
+ desc->rl_vlanctl = htole32(vlanctl);
+ desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr));
+ desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr));
+ cmdstat = segs[i].ds_len;
+ if (i != 0)
+ cmdstat |= RL_TDESC_CMD_OWN;
+ if (prod == sc->rl_ldata.rl_tx_desc_cnt - 1)
+ cmdstat |= RL_TDESC_CMD_EOR;
+ desc->rl_cmdstat = htole32(cmdstat | csum_flags);
+ sc->rl_ldata.rl_tx_free--;
+ }
+ /* Update producer index. */
+ sc->rl_ldata.rl_tx_prodidx = prod;
- /* Transfer ownership of packet to the chip. */
+ /* Set EOF on the last descriptor. */
+ ei = RL_TX_DESC_PRV(sc, prod);
+ desc = &sc->rl_ldata.rl_tx_list[ei];
+ desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF);
- sc->rl_ldata.rl_tx_list[arg.rl_idx].rl_cmdstat |=
- htole32(RL_TDESC_CMD_OWN);
- if (*idx != arg.rl_idx)
- sc->rl_ldata.rl_tx_list[*idx].rl_cmdstat |=
- htole32(RL_TDESC_CMD_OWN);
+ desc = &sc->rl_ldata.rl_tx_list[si];
+ /* Set SOF and transfer ownership of packet to the chip. */
+ desc->rl_cmdstat |= htole32(RL_TDESC_CMD_OWN | RL_TDESC_CMD_SOF);
- RL_DESC_INC(arg.rl_idx);
- *idx = arg.rl_idx;
+ /*
+ * Insure that the map for this transmission
+ * is placed at the array index of the last descriptor
+ * in this chain. (Swap last and first dmamaps.)
+ */
+ txd_last = &sc->rl_ldata.rl_tx_desc[ei];
+ map = txd->tx_dmamap;
+ txd->tx_dmamap = txd_last->tx_dmamap;
+ txd_last->tx_dmamap = map;
+ txd_last->tx_m = *m_head;
return (0);
}
static void
-re_tx_task(arg, npending)
- void *arg;
- int npending;
+re_tx_task(void *arg, int npending)
{
struct ifnet *ifp;
ifp = arg;
- NET_LOCK_GIANT();
re_start(ifp);
- NET_UNLOCK_GIANT();
-
- return;
}
/*
* Main transmit routine for C+ and gigE NICs.
*/
static void
-re_start(ifp)
- struct ifnet *ifp;
+re_start(struct ifnet *ifp)
{
struct rl_softc *sc;
- struct mbuf *m_head = NULL;
- int idx, queued = 0;
+ struct mbuf *m_head;
+ int queued;
sc = ifp->if_softc;
RL_LOCK(sc);
- if (!sc->rl_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) {
RL_UNLOCK(sc);
return;
}
- idx = sc->rl_ldata.rl_tx_prodidx;
-
- while (sc->rl_ldata.rl_tx_mbuf[idx] == NULL) {
+ for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
+ sc->rl_ldata.rl_tx_free > 1;) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
- if (re_encap(sc, &m_head, &idx)) {
+ if (re_encap(sc, &m_head) != 0) {
if (m_head == NULL)
break;
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
@@ -2370,7 +2673,7 @@ re_start(ifp)
if (queued == 0) {
#ifdef RE_TX_MODERATION
- if (sc->rl_ldata.rl_tx_free != RL_TX_DESC_CNT)
+ if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt)
CSR_WRITE_4(sc, RL_TIMERCNT, 1);
#endif
RL_UNLOCK(sc);
@@ -2383,8 +2686,6 @@ re_start(ifp)
sc->rl_ldata.rl_tx_list_map,
BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
- sc->rl_ldata.rl_tx_prodidx = idx;
-
CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
#ifdef RE_TX_MODERATION
@@ -2405,13 +2706,10 @@ re_start(ifp)
sc->rl_watchdog_timer = 5;
RL_UNLOCK(sc);
-
- return;
}
static void
-re_init(xsc)
- void *xsc;
+re_init(void *xsc)
{
struct rl_softc *sc = xsc;
@@ -2421,17 +2719,21 @@ re_init(xsc)
}
static void
-re_init_locked(sc)
- struct rl_softc *sc;
+re_init_locked(struct rl_softc *sc)
{
struct ifnet *ifp = sc->rl_ifp;
struct mii_data *mii;
- u_int32_t rxcfg = 0;
+ uint32_t reg;
+ uint16_t cfg;
+#ifndef __rtems__
union {
uint32_t align_dummy;
u_char eaddr[ETHER_ADDR_LEN];
} eaddr;
-
+#else
+ uint32_t eaddr[2];
+#endif
+
RL_LOCK_ASSERT(sc);
mii = device_get_softc(sc->rl_miibus);
@@ -2441,27 +2743,70 @@ re_init_locked(sc)
*/
re_stop(sc);
+ /* Put controller into known state. */
+ re_reset(sc);
+
/*
* Enable C+ RX and TX mode, as well as VLAN stripping and
* RX checksum offload. We must configure the C+ register
* before all others.
*/
- CSR_WRITE_2(sc, RL_CPLUS_CMD, RL_CPLUSCMD_RXENB|
- RL_CPLUSCMD_TXENB|RL_CPLUSCMD_PCI_MRW|
- RL_CPLUSCMD_VLANSTRIP|RL_CPLUSCMD_RXCSUM_ENB);
-
+ cfg = RL_CPLUSCMD_PCI_MRW;
+#ifndef __rtems__
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ cfg |= RL_CPLUSCMD_RXCSUM_ENB;
+ if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0)
+ cfg |= RL_CPLUSCMD_VLANSTRIP;
+ if ((sc->rl_flags & RL_FLAG_MACSTAT) != 0) {
+ cfg |= RL_CPLUSCMD_MACSTAT_DIS;
+ /* XXX magic. */
+ cfg |= 0x0001;
+ } else
+#endif
+ cfg |= RL_CPLUSCMD_RXENB | RL_CPLUSCMD_TXENB;
+ CSR_WRITE_2(sc, RL_CPLUS_CMD, cfg);
+ if (sc->rl_hwrev == RL_HWREV_8169_8110SC ||
+ sc->rl_hwrev == RL_HWREV_8169_8110SCE) {
+ reg = 0x000fff00;
+ if ((CSR_READ_1(sc, RL_CFG2) & RL_CFG2_PCI66MHZ) != 0)
+ reg |= 0x000000ff;
+ if (sc->rl_hwrev == RL_HWREV_8169_8110SCE)
+ reg |= 0x00f00000;
+ CSR_WRITE_4(sc, 0x7c, reg);
+ /* Disable interrupt mitigation. */
+ CSR_WRITE_2(sc, 0xe2, 0);
+ }
+ /*
+ * Disable TSO if interface MTU size is greater than MSS
+ * allowed in controller.
+ */
+#ifndef __rtems__
+ if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) {
+ ifp->if_capenable &= ~IFCAP_TSO4;
+ ifp->if_hwassist &= ~CSUM_TSO;
+ }
+#endif
+
/*
* 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.
*/
/* Copy MAC address on stack to align. */
+#ifndef __rtems__
bcopy(IF_LLADDR(ifp), eaddr.eaddr, ETHER_ADDR_LEN);
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
CSR_WRITE_4(sc, RL_IDR0,
htole32(*(u_int32_t *)(&eaddr.eaddr[0])));
CSR_WRITE_4(sc, RL_IDR4,
htole32(*(u_int32_t *)(&eaddr.eaddr[4])));
+#else
+ bzero(eaddr, sizeof(eaddr));
+ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN);
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
+ CSR_WRITE_4(sc, RL_IDR0, htole32(eaddr[0]));
+ CSR_WRITE_4(sc, RL_IDR4, htole32(eaddr[1]));
+#endif
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
/*
@@ -2490,7 +2835,7 @@ re_init_locked(sc)
CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);
/*
- * Set the initial TX and RX configuration.
+ * Set the initial TX configuration.
*/
if (sc->rl_testmode) {
if (sc->rl_type == RL_8169)
@@ -2504,32 +2849,10 @@ re_init_locked(sc)
CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16);
- CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG);
-
- /* Set the individual bit to receive frames for this host only. */
- rxcfg = CSR_READ_4(sc, RL_RXCFG);
- rxcfg |= RL_RXCFG_RX_INDIV;
-
- /* If we want promiscuous mode, set the allframes bit. */
- if (ifp->if_flags & IFF_PROMISC)
- rxcfg |= RL_RXCFG_RX_ALLPHYS;
- else
- rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
- CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
-
/*
- * Set capture broadcast bit to capture broadcast frames.
+ * Set the initial RX configuration.
*/
- if (ifp->if_flags & IFF_BROADCAST)
- rxcfg |= RL_RXCFG_RX_BROAD;
- else
- rxcfg &= ~RL_RXCFG_RX_BROAD;
- CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
-
- /*
- * Program the multicast filter, if necessary.
- */
- re_setmulti(sc);
+ re_set_rxmode(sc);
#ifdef DEVICE_POLLING
/*
@@ -2590,7 +2913,7 @@ re_init_locked(sc)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- sc->rl_link = 0;
+ sc->rl_flags &= ~RL_FLAG_LINK;
sc->rl_watchdog_timer = 0;
callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
}
@@ -2600,19 +2923,19 @@ re_init_locked(sc)
*/
#ifndef __rtems__
static int
-re_ifmedia_upd(ifp)
- struct ifnet *ifp;
+re_ifmedia_upd(struct ifnet *ifp)
{
struct rl_softc *sc;
struct mii_data *mii;
+ int error;
sc = ifp->if_softc;
mii = device_get_softc(sc->rl_miibus);
RL_LOCK(sc);
- mii_mediachg(mii);
+ error = mii_mediachg(mii);
RL_UNLOCK(sc);
- return (0);
+ return (error);
}
#endif
@@ -2621,9 +2944,7 @@ re_ifmedia_upd(ifp)
*/
#ifndef __rtems__
static void
-re_ifmedia_sts(ifp, ifmr)
- struct ifnet *ifp;
- struct ifmediareq *ifmr;
+re_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct rl_softc *sc;
struct mii_data *mii;
@@ -2653,10 +2974,25 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
switch (command) {
case SIOCSIFMTU:
- RL_LOCK(sc);
- if (ifr->ifr_mtu > RL_JUMBO_MTU)
+ if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > RL_JUMBO_MTU) {
+ error = EINVAL;
+ break;
+ }
+ if ((sc->rl_flags & RL_FLAG_NOJUMBO) != 0 &&
+ ifr->ifr_mtu > RL_MAX_FRAMELEN) {
error = EINVAL;
- ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ }
+ RL_LOCK(sc);
+ if (ifp->if_mtu != ifr->ifr_mtu)
+ ifp->if_mtu = ifr->ifr_mtu;
+#ifndef __rtems__
+ if (ifp->if_mtu > RL_TSO_MTU &&
+ (ifp->if_capenable & IFCAP_TSO4) != 0) {
+ ifp->if_capenable &= ~IFCAP_TSO4;
+ ifp->if_hwassist &= ~CSUM_TSO;
+ }
+#endif
RL_UNLOCK(sc);
break;
case SIOCSIFFLAGS:
@@ -2664,8 +3000,8 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
if ((ifp->if_flags & IFF_UP) != 0) {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
if (((ifp->if_flags ^ sc->rl_if_flags)
- & IFF_PROMISC) != 0)
- re_setmulti(sc);
+ & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
+ re_set_rxmode(sc);
} else
re_init_locked(sc);
} else {
@@ -2678,7 +3014,7 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
case SIOCADDMULTI:
case SIOCDELMULTI:
RL_LOCK(sc);
- re_setmulti(sc);
+ re_set_rxmode(sc);
RL_UNLOCK(sc);
break;
case SIOCGIFMEDIA:
@@ -2686,6 +3022,8 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
mii = device_get_softc(sc->rl_miibus);
#ifndef __rtems__
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+#else
+ error = rtems_mii_ioctl( RE_MDIO(sc), sc, command, &ifr->ifr_media);
#endif
break;
#ifndef __rtems__
@@ -2719,23 +3057,43 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
if (mask & IFCAP_HWCSUM) {
ifp->if_capenable ^= IFCAP_HWCSUM;
if (ifp->if_capenable & IFCAP_TXCSUM)
- ifp->if_hwassist = RE_CSUM_FEATURES;
+ ifp->if_hwassist |= RE_CSUM_FEATURES;
else
- ifp->if_hwassist = 0;
+ ifp->if_hwassist &= ~RE_CSUM_FEATURES;
reinit = 1;
}
if (mask & IFCAP_VLAN_HWTAGGING) {
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
reinit = 1;
}
+ if (mask & IFCAP_TSO4) {
+ ifp->if_capenable ^= IFCAP_TSO4;
+ if ((IFCAP_TSO4 & ifp->if_capenable) &&
+ (IFCAP_TSO4 & ifp->if_capabilities))
+ ifp->if_hwassist |= CSUM_TSO;
+ else
+ ifp->if_hwassist &= ~CSUM_TSO;
+ if (ifp->if_mtu > RL_TSO_MTU &&
+ (ifp->if_capenable & IFCAP_TSO4) != 0) {
+ ifp->if_capenable &= ~IFCAP_TSO4;
+ ifp->if_hwassist &= ~CSUM_TSO;
+ }
+ }
+ 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;
+ }
if (reinit && ifp->if_drv_flags & IFF_DRV_RUNNING)
re_init(sc);
-#ifdef VLAN_CAPABILITIES
VLAN_CAPABILITIES(ifp);
-#endif
}
-#endif
break;
+#endif
default:
error = ether_ioctl(ifp, command, data);
break;
@@ -2745,21 +3103,32 @@ re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
}
static void
-re_watchdog(sc)
- struct rl_softc *sc;
+re_watchdog(struct rl_softc *sc)
{
+ struct ifnet *ifp;
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++;
-
+ ifp = sc->rl_ifp;
re_txeof(sc);
- re_rxeof(sc);
+ if (sc->rl_ldata.rl_tx_free == sc->rl_ldata.rl_tx_desc_cnt) {
+ if_printf(ifp, "watchdog timeout (missed Tx interrupts) "
+ "-- recovering\n");
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
+ return;
+ }
+
+ if_printf(ifp, "watchdog timeout\n");
+ ifp->if_oerrors++;
+
+ re_rxeof(sc, NULL);
re_init_locked(sc);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
}
/*
@@ -2767,11 +3136,12 @@ re_watchdog(sc)
* RX and TX lists.
*/
static void
-re_stop(sc)
- struct rl_softc *sc;
+re_stop(struct rl_softc *sc)
{
- register int i;
+ int i;
struct ifnet *ifp;
+ struct rl_txdesc *txd;
+ struct rl_rxdesc *rxd;
RL_LOCK_ASSERT(sc);
@@ -2781,7 +3151,12 @@ re_stop(sc)
callout_stop(&sc->rl_stat_callout);
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- CSR_WRITE_1(sc, RL_COMMAND, 0x00);
+ if ((sc->rl_flags & RL_FLAG_CMDSTOP) != 0)
+ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_STOPREQ | RL_CMD_TX_ENB |
+ RL_CMD_RX_ENB);
+ else
+ CSR_WRITE_1(sc, RL_COMMAND, 0x00);
+ DELAY(1000);
CSR_WRITE_2(sc, RL_IMR, 0x0000);
CSR_WRITE_2(sc, RL_ISR, 0xFFFF);
@@ -2792,23 +3167,29 @@ re_stop(sc)
/* Free the TX list buffers. */
- for (i = 0; i < RL_TX_DESC_CNT; i++) {
- if (sc->rl_ldata.rl_tx_mbuf[i] != NULL) {
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_tx_dmamap[i]);
- m_freem(sc->rl_ldata.rl_tx_mbuf[i]);
- sc->rl_ldata.rl_tx_mbuf[i] = NULL;
+ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
+ txd = &sc->rl_ldata.rl_tx_desc[i];
+ if (txd->tx_m != NULL) {
+ bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag,
+ txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag,
+ txd->tx_dmamap);
+ m_freem(txd->tx_m);
+ txd->tx_m = NULL;
}
}
/* Free the RX list buffers. */
- for (i = 0; i < RL_RX_DESC_CNT; i++) {
- if (sc->rl_ldata.rl_rx_mbuf[i] != NULL) {
- bus_dmamap_unload(sc->rl_ldata.rl_mtag,
- sc->rl_ldata.rl_rx_dmamap[i]);
- m_freem(sc->rl_ldata.rl_rx_mbuf[i]);
- sc->rl_ldata.rl_rx_mbuf[i] = NULL;
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ rxd = &sc->rl_ldata.rl_rx_desc[i];
+ if (rxd->rx_m != NULL) {
+ bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag,
+ rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag,
+ rxd->rx_dmamap);
+ m_freem(rxd->rx_m);
+ rxd->rx_m = NULL;
}
}
}
@@ -2820,8 +3201,7 @@ re_stop(sc)
*/
#ifndef __rtems__
static int
-re_suspend(dev)
- device_t dev;
+re_suspend(device_t dev)
{
struct rl_softc *sc;
@@ -2829,6 +3209,7 @@ re_suspend(dev)
RL_LOCK(sc);
re_stop(sc);
+ re_setwol(sc);
sc->suspended = 1;
RL_UNLOCK(sc);
@@ -2843,8 +3224,7 @@ re_suspend(dev)
*/
#ifndef __rtems__
static int
-re_resume(dev)
- device_t dev;
+re_resume(device_t dev)
{
struct rl_softc *sc;
struct ifnet *ifp;
@@ -2854,11 +3234,22 @@ re_resume(dev)
RL_LOCK(sc);
ifp = sc->rl_ifp;
+ /* Take controller out of sleep mode. */
+ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
+ if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
+ CSR_WRITE_1(sc, RL_GPIO,
+ CSR_READ_1(sc, RL_GPIO) | 0x01);
+ }
/* reinitialize interface if necessary */
if (ifp->if_flags & IFF_UP)
re_init_locked(sc);
+ /*
+ * Clear WOL matching such that normal Rx filtering
+ * wouldn't interfere with WOL patterns.
+ */
+ re_clrwol(sc);
sc->suspended = 0;
RL_UNLOCK(sc);
@@ -2870,9 +3261,12 @@ re_resume(dev)
* Stop all chip I/O so that the kernel's probe routines don't
* get confused by errant DMAs when rebooting.
*/
+#ifndef __rtems__
+static int
+#else
static void
-re_shutdown(dev)
- device_t dev;
+#endif
+re_shutdown(device_t dev)
{
struct rl_softc *sc;
@@ -2886,5 +3280,108 @@ re_shutdown(dev)
* cases.
*/
sc->rl_ifp->if_flags &= ~IFF_UP;
+#ifndef __rtems__
+ re_setwol(sc);
+#endif
RL_UNLOCK(sc);
+
+#ifndef __rtems__
+ return (0);
+#endif
+}
+
+#ifndef __rtems__
+static void
+re_setwol(struct rl_softc *sc)
+{
+ struct ifnet *ifp;
+ int pmc;
+ uint16_t pmstat;
+ uint8_t v;
+
+ RL_LOCK_ASSERT(sc);
+
+ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+ return;
+
+ ifp = sc->rl_ifp;
+ /* Put controller into sleep mode. */
+ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
+ if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
+ CSR_WRITE_1(sc, RL_GPIO,
+ CSR_READ_1(sc, RL_GPIO) & ~0x01);
+ }
+ if ((ifp->if_capenable & IFCAP_WOL) != 0 &&
+ (sc->rl_flags & RL_FLAG_WOLRXENB) != 0)
+ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RX_ENB);
+ /* Enable config register write. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+ /* Enable PME. */
+ v = CSR_READ_1(sc, RL_CFG1);
+ v &= ~RL_CFG1_PME;
+ if ((ifp->if_capenable & IFCAP_WOL) != 0)
+ v |= RL_CFG1_PME;
+ CSR_WRITE_1(sc, RL_CFG1, v);
+
+ v = CSR_READ_1(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, RL_CFG3, v);
+
+ /* Config register write done. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ v = CSR_READ_1(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, RL_CFG5, v);
+
+ /*
+ * It seems that hardware resets its link speed to 100Mbps in
+ * power down mode so switching to 100Mbps in driver is not
+ * needed.
+ */
+
+ /* 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
+re_clrwol(struct rl_softc *sc)
+{
+ int pmc;
+ uint8_t v;
+
+ RL_LOCK_ASSERT(sc);
+
+ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+ return;
+
+ /* Enable config register write. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+ v = CSR_READ_1(sc, RL_CFG3);
+ v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+ CSR_WRITE_1(sc, RL_CFG3, v);
+
+ /* Config register write done. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ v = CSR_READ_1(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, RL_CFG5, v);
}
+#endif
diff --git a/bsd_eth_drivers/if_re/if_rl.c b/bsd_eth_drivers/if_re/if_rl.c
new file mode 100644
index 0000000..93ce2f7
--- /dev/null
+++ b/bsd_eth_drivers/if_re/if_rl.c
@@ -0,0 +1,2351 @@
+/*-
+ * Copyright (c) 1997, 1998
+ * Bill Paul <wpaul@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.
+ */
+
+#ifdef __rtems__
+#include <libbsdport.h>
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/pci/if_rl.c,v 1.189 2009/06/26 11:45:06 rwatson Exp $");
+
+/*
+ * 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@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 <sys/taskqueue.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/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"
+
+#ifdef __rtems__
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <libbsdport_post.h>
+struct mii_data
+{
+ int mii_media_status;
+ int mii_media_active;
+};
+#endif
+
+/*
+ * 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.
+ */
+#define RL_USEIOSPACE
+
+#ifndef __rtems__
+#include <pci/if_rlreg.h>
+#else
+#include "if_rlreg.h"
+#endif
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static 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_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 *);
+#ifndef __rtems__
+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 *);
+#else
+static int rl_ioctl(struct ifnet *, ioctl_command_t, caddr_t);
+static int rl_intr(void *);
+#endif
+static void rl_init(void *);
+static void rl_init_locked(struct rl_softc *sc);
+static void rl_mii_send(struct rl_softc *, uint32_t, int);
+static void rl_mii_sync(struct rl_softc *);
+static int rl_mii_readreg(struct rl_softc *, struct rl_mii_frame *);
+static int rl_mii_writereg(struct rl_softc *, struct rl_mii_frame *);
+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 *);
+#ifndef __rtems__
+static int rl_resume(device_t);
+#endif
+static int rl_rxeof(struct rl_softc *);
+static void rl_setmulti(struct rl_softc *);
+#ifndef __rtems__
+static int rl_shutdown(device_t);
+#else
+static void rl_shutdown(device_t);
+#endif
+static void rl_start(struct ifnet *);
+static void rl_start_locked(struct ifnet *);
+static void rl_stop(struct rl_softc *);
+#ifndef __rtems__
+static int rl_suspend(device_t);
+#endif
+static void rl_tick(void *);
+static void rl_txeof(struct rl_softc *);
+static void rl_watchdog(struct rl_softc *);
+
+#ifdef RL_USEIOSPACE
+#define RL_RES SYS_RES_IOPORT
+#define RL_RID RL_PCI_LOIO
+#else
+#define RL_RES SYS_RES_MEMORY
+#define RL_RID RL_PCI_LOMEM
+#endif
+
+#ifndef __rtems__
+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),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rl_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rl_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rl_miibus_statchg),
+
+ { 0, 0 }
+};
+
+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);
+#else
+static device_method_t rl_methods = {
+ probe: rl_probe,
+ attach: rl_attach,
+ shutdown: rl_shutdown,
+ detach: rl_detach,
+ irq_check_dis: 0,
+ irq_en: 0,
+};
+
+driver_t libbsdport_rl_driver = {
+ "rl",
+ &rl_methods,
+ DEV_TYPE_PCI,
+ sizeof(struct rl_softc)
+};
+
+static int mii_phy_probe_alloc (device_t* dev)
+{
+ struct mii_data* md;
+ int l;
+ l = sizeof(struct device) + 4 + DEVICE_SOFTC_ALIGNMENT;
+ *dev = malloc(l, M_DEVBUF, M_WAIT);
+ if (*dev == NULL)
+ return 1;
+ md = malloc(sizeof(struct mii_data), M_DEVBUF, M_WAIT);
+ if (md == NULL)
+ {
+ free (*dev, M_NOWAIT);
+ return 1;
+ }
+ memset (*dev, 0, l);
+ memset (md, 0, sizeof (struct mii_data));
+ memcpy (device_get_softc(*dev), &md, sizeof (md));
+ return 0;
+}
+
+#define mii_phy_probe(dev, miibus, ifmedia_upd, ifmedia_sts) mii_phy_probe_alloc(miibus)
+
+static int
+mdio_r(int phy, void *uarg, unsigned reg, uint32_t *pval)
+{
+struct rl_softc *sc = uarg;
+
+ if ( phy != 0 )
+ return EINVAL;
+
+ *pval = (uint32_t) rl_miibus_readreg(sc->rl_dev, phy, reg);
+
+ return 0;
+}
+
+static int
+mdio_w(int phy, void *uarg, unsigned reg, uint32_t val)
+{
+struct rl_softc *sc = uarg;
+
+ if ( phy != 0 )
+ return EINVAL;
+
+ rl_miibus_writereg(sc->rl_dev, phy, reg, val);
+
+ return 0;
+}
+
+struct rtems_mdio_info rl_mdio = {
+ mdio_r : mdio_r,
+ mdio_w : mdio_w,
+ has_gmii : 0
+};
+
+#define RE_MDIO(sc) (&rl_mdio)
+#endif
+
+#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;
+ }
+}
+
+/*
+ * MII access routines are provided for the 8129, which
+ * doesn't have a built-in PHY. For the 8139, we fake things
+ * up by diverting rl_phy_readreg()/rl_phy_writereg() to the
+ * direct access PHY registers.
+ */
+#define MII_SET(x) \
+ CSR_WRITE_1(sc, RL_MII, \
+ CSR_READ_1(sc, RL_MII) | (x))
+
+#define MII_CLR(x) \
+ CSR_WRITE_1(sc, RL_MII, \
+ CSR_READ_1(sc, RL_MII) & ~(x))
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void
+rl_mii_sync(struct rl_softc *sc)
+{
+ register int i;
+
+ MII_SET(RL_MII_DIR|RL_MII_DATAOUT);
+
+ for (i = 0; i < 32; i++) {
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ }
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void
+rl_mii_send(struct rl_softc *sc, uint32_t bits, int cnt)
+{
+ int i;
+
+ MII_CLR(RL_MII_CLK);
+
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
+ if (bits & i) {
+ MII_SET(RL_MII_DATAOUT);
+ } else {
+ MII_CLR(RL_MII_DATAOUT);
+ }
+ DELAY(1);
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ MII_SET(RL_MII_CLK);
+ }
+}
+
+/*
+ * Read an PHY register through the MII.
+ */
+static int
+rl_mii_readreg(struct rl_softc *sc, struct rl_mii_frame *frame)
+{
+ int i, ack;
+
+ /* Set up frame for RX. */
+ frame->mii_stdelim = RL_MII_STARTDELIM;
+ frame->mii_opcode = RL_MII_READOP;
+ frame->mii_turnaround = 0;
+ frame->mii_data = 0;
+
+ CSR_WRITE_2(sc, RL_MII, 0);
+
+ /* Turn on data xmit. */
+ MII_SET(RL_MII_DIR);
+
+ rl_mii_sync(sc);
+
+ /* Send command/address info. */
+ rl_mii_send(sc, frame->mii_stdelim, 2);
+ rl_mii_send(sc, frame->mii_opcode, 2);
+ rl_mii_send(sc, frame->mii_phyaddr, 5);
+ rl_mii_send(sc, frame->mii_regaddr, 5);
+
+ /* Idle bit */
+ MII_CLR((RL_MII_CLK|RL_MII_DATAOUT));
+ DELAY(1);
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+
+ /* Turn off xmit. */
+ MII_CLR(RL_MII_DIR);
+
+ /* Check for ack */
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ ack = CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN;
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack) {
+ for(i = 0; i < 16; i++) {
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+ }
+ goto fail;
+ }
+
+ for (i = 0x8000; i; i >>= 1) {
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ if (!ack) {
+ if (CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN)
+ frame->mii_data |= i;
+ DELAY(1);
+ }
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+ }
+
+fail:
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+
+ return (ack ? 1 : 0);
+}
+
+/*
+ * Write to a PHY register through the MII.
+ */
+static int
+rl_mii_writereg(struct rl_softc *sc, struct rl_mii_frame *frame)
+{
+
+ /* Set up frame for TX. */
+ frame->mii_stdelim = RL_MII_STARTDELIM;
+ frame->mii_opcode = RL_MII_WRITEOP;
+ frame->mii_turnaround = RL_MII_TURNAROUND;
+
+ /* Turn on data output. */
+ MII_SET(RL_MII_DIR);
+
+ rl_mii_sync(sc);
+
+ rl_mii_send(sc, frame->mii_stdelim, 2);
+ rl_mii_send(sc, frame->mii_opcode, 2);
+ rl_mii_send(sc, frame->mii_phyaddr, 5);
+ rl_mii_send(sc, frame->mii_regaddr, 5);
+ rl_mii_send(sc, frame->mii_turnaround, 2);
+ rl_mii_send(sc, frame->mii_data, 16);
+
+ /* Idle bit. */
+ MII_SET(RL_MII_CLK);
+ DELAY(1);
+ MII_CLR(RL_MII_CLK);
+ DELAY(1);
+
+ /* Turn off xmit. */
+ MII_CLR(RL_MII_DIR);
+
+ return (0);
+}
+
+static int
+rl_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct rl_softc *sc;
+ struct rl_mii_frame frame;
+ uint16_t rval = 0;
+ uint16_t rl8139_reg = 0;
+
+ sc = device_get_softc(dev);
+
+ if (sc->rl_type == RL_8139) {
+ /* Pretend the internal PHY is only at address 0 */
+ if (phy) {
+ return (0);
+ }
+ 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:
+ rval = CSR_READ_1(sc, RL_MEDIASTAT);
+ return (rval);
+ default:
+ device_printf(sc->rl_dev, "bad phy register\n");
+ return (0);
+ }
+ rval = CSR_READ_2(sc, rl8139_reg);
+ return (rval);
+ }
+
+ bzero((char *)&frame, sizeof(frame));
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ rl_mii_readreg(sc, &frame);
+
+ return (frame.mii_data);
+}
+
+static int
+rl_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct rl_softc *sc;
+ struct rl_mii_frame frame;
+ uint16_t rl8139_reg = 0;
+
+ sc = device_get_softc(dev);
+
+ if (sc->rl_type == RL_8139) {
+ /* Pretend the internal PHY is only at address 0 */
+ if (phy) {
+ return (0);
+ }
+ 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);
+ }
+
+ bzero((char *)&frame, sizeof(frame));
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ frame.mii_data = data;
+ rl_mii_writereg(sc, &frame);
+
+ return (0);
+}
+
+#ifdef __rtems__
+static void
+rl_mii_tick(device_t dev)
+{
+ struct rl_softc *sc;
+ struct mii_data *mii;
+ int err;
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->rl_miibus);
+ mii->mii_media_status = IFM_MAKEWORD(0,0,0,0);
+ mii->mii_media_active = IFM_100_TX;
+ if ( (err = rtems_mii_ioctl( RE_MDIO(sc), sc, SIOCGIFMEDIA, &mii->mii_media_status)) ) {
+ device_printf(sc->rl_dev, "WARNING: mii ioctl failed; unable to determine link status -- fake ON\n");
+ mii->mii_media_status = IFM_LINK_OK;
+ }
+}
+#endif
+
+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_setmulti(struct rl_softc *sc)
+{
+ struct ifnet *ifp = sc->rl_ifp;
+ int h = 0;
+ uint32_t hashes[2] = { 0, 0 };
+#ifndef __rtems__
+ struct ifmultiaddr *ifma;
+#endif
+ uint32_t rxfilt;
+ int mcnt = 0;
+
+ RL_LOCK_ASSERT(sc);
+
+ rxfilt = CSR_READ_4(sc, RL_RXCFG);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxfilt |= RL_RXCFG_RX_MULTI;
+ CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
+ CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF);
+ CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, RL_MAR0, 0);
+ CSR_WRITE_4(sc, RL_MAR4, 0);
+
+#ifndef __rtems__
+ /* 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));
+ mcnt++;
+ }
+ if_maddr_runlock(ifp);
+#else
+ {
+ struct in_ifaddr *ia;
+ IFP_TO_IA(ifp, ia);
+ if (ia) {
+ struct in_multi *inm;
+ struct in_multistep step;
+ step.i_ia = ia;
+ step.i_inm = NULL;
+ IN_NEXT_MULTI(step, inm);
+ while (inm) {
+ unsigned char ethaddr[ETHER_ADDR_LEN];
+ unsigned char* ipaddr = (unsigned char *) &inm->inm_addr.s_addr;
+ ETHER_MAP_IP_MULTICAST (ipaddr, ethaddr);
+ h = ether_crc32_be(ethaddr, ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ IN_NEXT_MULTI(step, inm);
+ }
+ }
+ }
+#endif
+
+ if (mcnt)
+ rxfilt |= RL_RXCFG_RX_MULTI;
+ else
+ rxfilt &= ~RL_RXCFG_RX_MULTI;
+
+ CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
+ CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
+}
+
+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)
+{
+ 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;
+ struct rl_type *t;
+#ifndef __rtems__
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *children;
+#endif
+ int error = 0, i, rid;
+ int 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);
+#ifndef __rtems__
+ 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, "");
+#endif
+
+ 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. */
+ rid = RL_RID;
+ sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, 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;
+ }
+
+ /*
+ * 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;
+ }
+
+ /* Do MII setup */
+ if (mii_phy_probe(dev, &sc->rl_miibus,
+ rl_ifmedia_upd, rl_ifmedia_sts)) {
+ device_printf(dev, "MII without any phy!\n");
+ error = ENXIO;
+ 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;
+#ifndef __rtems__
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+ ifp->if_capenable = ifp->if_capabilities;
+#endif
+#ifdef DEVICE_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+#ifdef __rtems__
+ taskqueue_create_fast("rl_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &taskqueue_fast);
+ taskqueue_start_threads(&taskqueue_fast, 1, PI_NET, "%s taskq",
+ device_get_nameunit(dev));
+#endif
+
+ /*
+ * Call MI attach routine.
+ */
+ ether_ifattach(ifp, eaddr);
+
+ /* Hook interrupt last to avoid having to lock softc */
+#ifndef __rtems__
+ error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, rl_intr, sc, &sc->rl_intrhand[0]);
+#else
+ error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE,
+ rl_intr, NULL, sc, &sc->rl_intrhand[0]);
+#endif
+ 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)
+ {
+#ifndef __rtems__
+ device_delete_child(dev, sc->rl_miibus);
+#else
+ free (sc->rl_miibus, M_NOWAIT);
+#endif
+ }
+ 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, RL_RES, RL_RID, 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++;
+ 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((char*)rxbufpos, total_len, RL_ETHER_ALIGN, ifp,
+ NULL);
+ if (m != NULL)
+ m_copyback(m, wrap, total_len - wrap,
+ (caddr_t)sc->rl_cdata.rl_rx_buf);
+ cur_rx = (total_len - wrap + ETHER_CRC_LEN);
+ } else {
+ m = m_devget((char*)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);
+#ifndef __rtems__
+ (*ifp->if_input)(ifp, m);
+#else
+ ether_input_skipping(ifp, m);
+#endif
+ 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 */
+ 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@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);
+#ifndef __rtems__
+ mii_tick(mii);
+#else
+ rl_mii_tick(sc->rl_dev);
+#endif
+ 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)
+ rl_init_locked(sc);
+ }
+ return (rx_npkts);
+}
+#endif /* DEVICE_POLLING */
+
+#ifndef __rtems__
+static void
+#else
+static int
+#endif
+rl_intr(void *arg)
+{
+ struct rl_softc *sc = arg;
+ struct ifnet *ifp = sc->rl_ifp;
+ uint16_t status;
+
+ RL_LOCK(sc);
+
+ if (sc->suspended)
+ goto done_locked;
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING)
+ goto done_locked;
+#endif
+
+ for (;;) {
+ status = CSR_READ_2(sc, RL_ISR);
+ /* If the card has gone away, the read returns 0xffff. */
+ if (status == 0xffff)
+#ifdef __rtems__
+ return FILTER_STRAY;
+#else
+ break;
+#endif
+ if (status != 0)
+ CSR_WRITE_2(sc, RL_ISR, status);
+ if ((status & RL_INTRS) == 0)
+ break;
+ if (status & RL_ISR_RX_OK)
+ rl_rxeof(sc);
+ if (status & RL_ISR_RX_ERR)
+ rl_rxeof(sc);
+ if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR))
+ rl_txeof(sc);
+ if (status & RL_ISR_SYSTEM_ERR)
+ rl_init_locked(sc);
+ }
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ rl_start_locked(ifp);
+
+done_locked:
+ RL_UNLOCK(sc);
+
+#ifdef __rtems__
+ return FILTER_HANDLED;
+#endif
+}
+
+/*
+ * 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_DONTWAIT);
+ 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);
+
+#ifdef __rtems__
+ if ((sc->rl_flags & RL_FLAG_LINK) == 0) {
+ rl_mii_tick(sc->rl_dev);
+ rl_miibus_statchg(sc->rl_dev);
+ }
+#endif
+
+ 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 rxcfg = 0;
+ uint32_t eaddr[2];
+
+ RL_LOCK_ASSERT(sc);
+
+ mii = device_get_softc(sc->rl_miibus);
+
+ /*
+ * 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 the individual bit to receive frames for this host only. */
+ rxcfg = CSR_READ_4(sc, RL_RXCFG);
+ rxcfg |= RL_RXCFG_RX_INDIV;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= RL_RXCFG_RX_ALLPHYS;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+ } else {
+ rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+ }
+
+ /* Set capture broadcast bit to capture broadcast frames. */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ rxcfg |= RL_RXCFG_RX_BROAD;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+ } else {
+ rxcfg &= ~RL_RXCFG_RX_BROAD;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+ }
+
+ /* Program the multicast filter, if necessary. */
+ rl_setmulti(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, 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.
+ */
+#ifndef __rtems__
+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);
+}
+#endif
+
+/*
+ * Report current media status.
+ */
+#ifndef __rtems__
+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);
+ RL_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+#endif
+
+static int
+#ifndef __rtems__
+rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+#else
+rl_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+#endif
+{
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ struct rl_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ RL_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ rl_init_locked(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ rl_stop(sc);
+ }
+ RL_UNLOCK(sc);
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+#ifdef __rtems__
+ if ( ETHER_SIOCMULTIFRAG(error, command, ifr, ifp) )
+ break;
+#endif
+ RL_LOCK(sc);
+ rl_setmulti(sc);
+ RL_UNLOCK(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = device_get_softc(sc->rl_miibus);
+#ifndef __rtems__
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+#else
+ error = rtems_mii_ioctl( RE_MDIO(sc), sc, command, &ifr->ifr_media);
+#endif
+ break;
+#ifndef __rtems__
+ case SIOCSIFCAP:
+#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 */
+ break;
+#endif
+ 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);
+ 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) {
+ 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.
+ */
+#ifndef __rtems__
+static int
+rl_suspend(device_t dev)
+{
+ struct rl_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ RL_LOCK(sc);
+ rl_stop(sc);
+ sc->suspended = 1;
+ RL_UNLOCK(sc);
+
+ return (0);
+}
+#endif
+
+/*
+ * Device resume routine. Restore some PCI settings in case the BIOS
+ * doesn't, re-enable busmastering, and restart the interface if
+ * appropriate.
+ */
+#ifndef __rtems__
+static int
+rl_resume(device_t dev)
+{
+ struct rl_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = sc->rl_ifp;
+
+ RL_LOCK(sc);
+
+ /* reinitialize interface if necessary */
+ if (ifp->if_flags & IFF_UP)
+ rl_init_locked(sc);
+
+ sc->suspended = 0;
+
+ RL_UNLOCK(sc);
+
+ return (0);
+}
+#endif
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+#ifndef __rtems__
+static int
+#else
+static void
+#endif
+rl_shutdown(device_t dev)
+{
+ struct rl_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ RL_LOCK(sc);
+ rl_stop(sc);
+ RL_UNLOCK(sc);
+
+#ifndef __rtems__
+ return (0);
+#endif
+}
diff --git a/bsd_eth_drivers/if_re/if_rlreg.h b/bsd_eth_drivers/if_re/if_rlreg.h
index 5d9db5d..8c41a72 100644
--- a/bsd_eth_drivers/if_re/if_rlreg.h
+++ b/bsd_eth_drivers/if_re/if_rlreg.h
@@ -29,7 +29,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/sys/pci/if_rlreg.h,v 1.51.2.14.2.1 2008/10/02 02:57:24 kensmith Exp $
+ * $FreeBSD: src/sys/pci/if_rlreg.h,v 1.98 2009/08/24 18:58:13 yongari Exp $
*/
/*
@@ -76,7 +76,11 @@
#define RL_EECMD 0x0050 /* EEPROM command register */
#define RL_CFG0 0x0051 /* config register #0 */
#define RL_CFG1 0x0052 /* config register #1 */
- /* 0053-0057 reserved */
+#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 */
@@ -127,8 +131,10 @@
#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_MAXRXPKTLEN 0x00DA /* 16 bits, chip multiplies by 8 */
-#define RL_GTXSTART 0x0038 /* 16 bits */
+#define RL_GTXSTART 0x0038 /* 8 bits */
/*
* TX config register bits
@@ -148,15 +154,23 @@
/* Known revision codes. */
#define RL_HWREV_8169 0x00000000
-#define RL_HWREV_8110S 0x00800000
-#define RL_HWREV_8169S 0x04000000
+#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_8102EL 0x24800000
+#define RL_HWREV_8102EL_SPIN1 0x24c00000
+#define RL_HWREV_8168D 0x28000000
+#define RL_HWREV_8168DP 0x28800000
#define RL_HWREV_8168_SPIN1 0x30000000
#define RL_HWREV_8100E 0x30800000
#define RL_HWREV_8101E 0x34000000
+#define RL_HWREV_8102E 0x34800000
#define RL_HWREV_8168_SPIN2 0x38000000
#define RL_HWREV_8168_SPIN3 0x38400000
+#define RL_HWREV_8168C 0x3C000000
+#define RL_HWREV_8168C_SPIN2 0x3C400000
+#define RL_HWREV_8168CP 0x3C800000
#define RL_HWREV_8139 0x60000000
#define RL_HWREV_8139A 0x70000000
#define RL_HWREV_8139AG 0x70800000
@@ -167,6 +181,8 @@
#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
@@ -297,8 +313,30 @@
#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 */
@@ -359,16 +397,51 @@
* 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_FAST_B2B 0x01
+
+/*
+ * Config 4 register
+ */
+#define RL_CFG4_LWPTN 0x04
+#define RL_CFG4_LWPME 0x10
+
+/*
+ * 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
*/
@@ -403,6 +476,15 @@
#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 */
@@ -447,6 +529,11 @@
#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
@@ -456,16 +543,29 @@
#define RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ)
#define RL_TXCFG_CONFIG (RL_TXCFG_IFG|RL_TX_MAXDMA)
+#ifndef __rtems__
#define RL_ETHER_ALIGN 2
+#else
+#define RL_ETHER_ALIGN 0
+#endif
+
+/*
+ * 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;
- bus_dmamap_t rl_rx_dmamap;
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;
};
@@ -544,6 +644,7 @@ struct rl_desc {
#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 */
@@ -552,6 +653,10 @@ struct rl_desc {
#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
/*
* Error bits are valid only on the last descriptor of a frame
@@ -589,6 +694,8 @@ struct rl_desc {
#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 */
@@ -600,6 +707,9 @@ struct rl_desc {
#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
@@ -635,24 +745,35 @@ struct rl_stats {
/*
* Rx/Tx descriptor parameters (8139C+ and 8169 only)
*
- * Tx/Rx count must be equal. Shared code like re_dma_map_desc assumes this.
- * Buffers must be a multiple of 8 bytes. Currently limit to 64 descriptors
- * due to the 8139C+. We need to put the number of descriptors in the ring
- * structure and use that value instead.
+ * 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.
*/
-#if !defined(__i386__) && !defined(__amd64__)
+#ifndef __NO_STRICT_ALIGNMENT
#define RE_FIXUP_RX 1
#endif
-#define RL_TX_DESC_CNT 64
-#define RL_TX_DESC_THLD 4
-#define RL_RX_DESC_CNT RL_TX_DESC_CNT
+#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_NTXSEGS 32
-#define RL_RX_LIST_SZ (RL_RX_DESC_CNT * sizeof(struct rl_desc))
-#define RL_TX_LIST_SZ (RL_TX_DESC_CNT * sizeof(struct rl_desc))
#define RL_RING_ALIGN 256
#define RL_IFQ_MAXLEN 512
-#define RL_DESC_INC(x) (x = (x + 1) % RL_TX_DESC_CNT)
+#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*/)
@@ -664,32 +785,48 @@ struct rl_stats {
#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_HDR_LEN-ETHER_CRC_LEN)
+#define RL_MAX_FRAMELEN \
+ (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN)
-struct rl_softc;
+struct rl_txdesc {
+ struct mbuf *tx_m;
+ bus_dmamap_t tx_dmamap;
+};
-struct rl_dmaload_arg {
- int rl_idx;
- int rl_maxsegs;
- uint32_t rl_flags;
- struct rl_desc *rl_ring;
+struct rl_rxdesc {
+ struct mbuf *rx_m;
+ bus_dmamap_t rx_dmamap;
+ bus_size_t rx_size;
};
struct rl_list_data {
- struct mbuf *rl_tx_mbuf[RL_TX_DESC_CNT];
- struct mbuf *rl_rx_mbuf[RL_RX_DESC_CNT];
+ struct rl_txdesc rl_tx_desc[RL_TX_DESC_CNT];
+ struct rl_rxdesc rl_rx_desc[RL_RX_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_dmamap_t rl_tx_dmamap[RL_TX_DESC_CNT];
- bus_dmamap_t rl_rx_dmamap[RL_RX_DESC_CNT];
- bus_dma_tag_t rl_mtag; /* mbuf mapping tag */
+ bus_dma_tag_t rl_tx_mtag; /* mbuf TX mapping tag */
+ bus_dma_tag_t rl_rx_mtag; /* mbuf RX mapping tag */
+ bus_dmamap_t rl_rx_sparemap;
bus_dma_tag_t rl_stag; /* stats mapping tag */
bus_dmamap_t rl_smap; /* stats map */
struct rl_stats *rl_stats;
@@ -704,17 +841,20 @@ struct rl_list_data {
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;
- struct resource *rl_irq;
- void *rl_intrhand;
+ int rl_res_id;
+ int rl_res_type;
+ struct resource *rl_irq[RL_MSI_MESSAGES];
+ void *rl_intrhand[RL_MSI_MESSAGES];
device_t rl_miibus;
bus_dma_tag_t rl_parent_tag;
- bus_dma_tag_t rl_tag;
uint8_t rl_type;
int rl_eecmd_read;
int rl_eewidth;
@@ -731,6 +871,10 @@ struct rl_softc {
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;
@@ -739,9 +883,22 @@ struct rl_softc {
struct task rl_txtask;
struct task rl_inttask;
- struct mtx rl_intlock;
int rl_txstart;
- int rl_link;
+ uint32_t rl_flags;
+#define RL_FLAG_MSI 0x0001
+#define RL_FLAG_AUTOPAD 0x0002
+#define RL_FLAG_PHYWAKE 0x0008
+#define RL_FLAG_NOJUMBO 0x0010
+#define RL_FLAG_PAR 0x0020
+#define RL_FLAG_DESCV2 0x0040
+#define RL_FLAG_MACSTAT 0x0080
+#define RL_FLAG_FASTETHER 0x0100
+#define RL_FLAG_CMDSTOP 0x0200
+#define RL_FLAG_MACRESET 0x0400
+#define RL_FLAG_WOLRXENB 0x1000
+#define RL_FLAG_MACSLEEP 0x2000
+#define RL_FLAG_PCIE 0x4000
+#define RL_FLAG_LINK 0x8000
};
#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx)
@@ -786,6 +943,7 @@ struct rl_softc {
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.