summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/re/if_re.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/re/if_re.c')
-rw-r--r--freebsd/sys/dev/re/if_re.c1181
1 files changed, 904 insertions, 277 deletions
diff --git a/freebsd/sys/dev/re/if_re.c b/freebsd/sys/dev/re/if_re.c
index 2327aa1f..a290d73c 100644
--- a/freebsd/sys/dev/re/if_re.c
+++ b/freebsd/sys/dev/re/if_re.c
@@ -159,8 +159,12 @@ MODULE_DEPEND(re, miibus, 1, 1, 1);
#include <rtems/bsd/local/miibus_if.h>
/* Tunables. */
+static int intr_filter = 0;
+TUNABLE_INT("hw.re.intr_filter", &intr_filter);
static int msi_disable = 0;
TUNABLE_INT("hw.re.msi_disable", &msi_disable);
+static int msix_disable = 0;
+TUNABLE_INT("hw.re.msix_disable", &msix_disable);
static int prefer_iomap = 0;
TUNABLE_INT("hw.re.prefer_iomap", &prefer_iomap);
@@ -169,15 +173,17 @@ TUNABLE_INT("hw.re.prefer_iomap", &prefer_iomap);
/*
* Various supported device vendors/types and their names.
*/
-static struct rl_type re_devs[] = {
+static const struct rl_type re_devs[] = {
{ DLINK_VENDORID, DLINK_DEVICEID_528T, 0,
"D-Link DGE-528(T) Gigabit Ethernet Adapter" },
+ { DLINK_VENDORID, DLINK_DEVICEID_530T_REVC, 0,
+ "D-Link DGE-530(T) Gigabit Ethernet Adapter" },
{ RT_VENDORID, RT_DEVICEID_8139, 0,
"RealTek 8139C+ 10/100BaseTX" },
{ RT_VENDORID, RT_DEVICEID_8101E, 0,
- "RealTek 8101E/8102E/8102EL/8103E PCIe 10/100baseTX" },
+ "RealTek 810xE PCIe 10/100baseTX" },
{ RT_VENDORID, RT_DEVICEID_8168, 0,
- "RealTek 8168/8111 B/C/CP/D/DP/E PCIe Gigabit Ethernet" },
+ "RealTek 8168/8111 B/C/CP/D/DP/E/F 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,
@@ -190,40 +196,47 @@ static struct rl_type re_devs[] = {
"US Robotics 997902 (RTL8169S) Gigabit Ethernet" }
};
-static struct rl_hwrev re_hwrevs[] = {
- { RL_HWREV_8139, RL_8139, "" },
- { RL_HWREV_8139A, RL_8139, "A" },
- { RL_HWREV_8139AG, RL_8139, "A-G" },
- { RL_HWREV_8139B, RL_8139, "B" },
- { RL_HWREV_8130, RL_8139, "8130" },
- { RL_HWREV_8139C, RL_8139, "C" },
- { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C" },
- { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+"},
- { RL_HWREV_8168_SPIN1, RL_8169, "8168"},
- { 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/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_8103E, RL_8169, "8103E"},
- { 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"},
- { RL_HWREV_8168E, RL_8169, "8168E/8111E"},
- { 0, 0, NULL }
+static const struct rl_hwrev re_hwrevs[] = {
+ { RL_HWREV_8139, RL_8139, "", RL_MTU },
+ { RL_HWREV_8139A, RL_8139, "A", RL_MTU },
+ { RL_HWREV_8139AG, RL_8139, "A-G", RL_MTU },
+ { RL_HWREV_8139B, RL_8139, "B", RL_MTU },
+ { RL_HWREV_8130, RL_8139, "8130", RL_MTU },
+ { RL_HWREV_8139C, RL_8139, "C", RL_MTU },
+ { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C", RL_MTU },
+ { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+", RL_MTU },
+ { RL_HWREV_8168B_SPIN1, RL_8169, "8168", RL_JUMBO_MTU },
+ { RL_HWREV_8169, RL_8169, "8169", RL_JUMBO_MTU },
+ { RL_HWREV_8169S, RL_8169, "8169S", RL_JUMBO_MTU },
+ { RL_HWREV_8110S, RL_8169, "8110S", RL_JUMBO_MTU },
+ { RL_HWREV_8169_8110SB, RL_8169, "8169SB/8110SB", RL_JUMBO_MTU },
+ { RL_HWREV_8169_8110SC, RL_8169, "8169SC/8110SC", RL_JUMBO_MTU },
+ { RL_HWREV_8169_8110SBL, RL_8169, "8169SBL/8110SBL", RL_JUMBO_MTU },
+ { RL_HWREV_8169_8110SCE, RL_8169, "8169SC/8110SC", RL_JUMBO_MTU },
+ { RL_HWREV_8100, RL_8139, "8100", RL_MTU },
+ { RL_HWREV_8101, RL_8139, "8101", RL_MTU },
+ { RL_HWREV_8100E, RL_8169, "8100E", RL_MTU },
+ { RL_HWREV_8101E, RL_8169, "8101E", RL_MTU },
+ { RL_HWREV_8102E, RL_8169, "8102E", RL_MTU },
+ { RL_HWREV_8102EL, RL_8169, "8102EL", RL_MTU },
+ { RL_HWREV_8102EL_SPIN1, RL_8169, "8102EL", RL_MTU },
+ { RL_HWREV_8103E, RL_8169, "8103E", RL_MTU },
+ { RL_HWREV_8401E, RL_8169, "8401E", RL_MTU },
+ { RL_HWREV_8402, RL_8169, "8402", RL_MTU },
+ { RL_HWREV_8105E, RL_8169, "8105E", RL_MTU },
+ { RL_HWREV_8105E_SPIN1, RL_8169, "8105E", RL_MTU },
+ { RL_HWREV_8168B_SPIN2, RL_8169, "8168", RL_JUMBO_MTU },
+ { RL_HWREV_8168B_SPIN3, RL_8169, "8168", RL_JUMBO_MTU },
+ { RL_HWREV_8168C, RL_8169, "8168C/8111C", RL_JUMBO_MTU_6K },
+ { RL_HWREV_8168C_SPIN2, RL_8169, "8168C/8111C", RL_JUMBO_MTU_6K },
+ { RL_HWREV_8168CP, RL_8169, "8168CP/8111CP", RL_JUMBO_MTU_6K },
+ { RL_HWREV_8168D, RL_8169, "8168D/8111D", RL_JUMBO_MTU_9K },
+ { RL_HWREV_8168DP, RL_8169, "8168DP/8111DP", RL_JUMBO_MTU_9K },
+ { RL_HWREV_8168E, RL_8169, "8168E/8111E", RL_JUMBO_MTU_9K},
+ { RL_HWREV_8168E_VL, RL_8169, "8168E/8111E-VL", RL_JUMBO_MTU_6K},
+ { RL_HWREV_8168F, RL_8169, "8168F/8111F", RL_JUMBO_MTU_9K},
+ { RL_HWREV_8411, RL_8169, "8411", RL_JUMBO_MTU_9K},
+ { 0, 0, NULL, 0 }
};
static int re_probe (device_t);
@@ -237,7 +250,9 @@ static int re_allocmem (device_t, struct rl_softc *);
static __inline void re_discard_rxbuf
(struct rl_softc *, int);
static int re_newbuf (struct rl_softc *, int);
+static int re_jumbo_newbuf (struct rl_softc *, int);
static int re_rx_list_init (struct rl_softc *);
+static int re_jrx_list_init (struct rl_softc *);
static int re_tx_list_init (struct rl_softc *);
#ifdef RE_FIXUP_RX
static __inline void re_fixup_rx
@@ -250,10 +265,11 @@ static int re_poll (struct ifnet *, enum poll_cmd, int);
static int re_poll_locked (struct ifnet *, enum poll_cmd, int);
#endif
static int re_intr (void *);
+static void re_intr_msi (void *);
static void re_tick (void *);
-static void re_tx_task (void *, int);
static void re_int_task (void *, int);
static void re_start (struct ifnet *);
+static void re_start_locked (struct ifnet *);
static int re_ioctl (struct ifnet *, u_long, caddr_t);
static void re_init (void *);
static void re_init_locked (struct rl_softc *);
@@ -275,10 +291,12 @@ static int re_miibus_readreg (device_t, int, int);
static int re_miibus_writereg (device_t, int, int, int);
static void re_miibus_statchg (device_t);
+static void re_set_jumbo (struct rl_softc *, int);
static void re_set_rxmode (struct rl_softc *);
static void re_reset (struct rl_softc *);
static void re_setwol (struct rl_softc *);
static void re_clrwol (struct rl_softc *);
+static void re_set_linkspeed (struct rl_softc *);
#ifdef RE_DIAG
static int re_diag (struct rl_softc *);
@@ -286,6 +304,8 @@ static int re_diag (struct rl_softc *);
static void re_add_sysctls (struct rl_softc *);
static int re_sysctl_stats (SYSCTL_HANDLER_ARGS);
+static int sysctl_int_range (SYSCTL_HANDLER_ARGS, int, int);
+static int sysctl_hw_re_int_mod (SYSCTL_HANDLER_ARGS);
static device_method_t re_methods[] = {
/* Device interface */
@@ -296,16 +316,12 @@ static device_method_t re_methods[] = {
DEVMETHOD(device_resume, re_resume),
DEVMETHOD(device_shutdown, re_shutdown),
- /* bus interface */
- DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_driver_added, bus_generic_driver_added),
-
/* MII interface */
DEVMETHOD(miibus_readreg, re_miibus_readreg),
DEVMETHOD(miibus_writereg, re_miibus_writereg),
DEVMETHOD(miibus_statchg, re_miibus_statchg),
- { 0, 0 }
+ DEVMETHOD_END
};
static driver_t re_driver = {
@@ -700,7 +716,7 @@ re_reset(struct rl_softc *sc)
if ((sc->rl_flags & RL_FLAG_MACRESET) != 0)
CSR_WRITE_1(sc, 0x82, 1);
- if (sc->rl_hwrev == RL_HWREV_8169S)
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8169S)
re_gmii_writereg(sc->rl_dev, 1, 0x0b, 0);
}
@@ -855,7 +871,7 @@ re_diag(struct rl_softc *sc)
device_printf(sc->rl_dev, "expected TX data: %6D/%6D/0x%x\n",
dst, ":", src, ":", ETHERTYPE_IP);
device_printf(sc->rl_dev, "received RX data: %6D/%6D/0x%x\n",
- eh->ether_dhost, ":", eh->ether_shost, ":",
+ eh->ether_dhost, ":", eh->ether_shost, ":",
ntohs(eh->ether_type));
device_printf(sc->rl_dev, "You may have a defective 32-bit "
"NIC plugged into a 64-bit PCI slot.\n");
@@ -890,7 +906,7 @@ done:
static int
re_probe(device_t dev)
{
- struct rl_type *t;
+ const struct rl_type *t;
uint16_t devid, vendor;
uint16_t revid, sdevid;
int i;
@@ -992,6 +1008,17 @@ re_allocmem(device_t dev, struct rl_softc *sc)
* Allocate map for RX mbufs.
*/
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
+ error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t),
+ 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ MJUM9BYTES, 1, MJUM9BYTES, 0, NULL, NULL,
+ &sc->rl_ldata.rl_jrx_mtag);
+ if (error) {
+ device_printf(dev,
+ "could not allocate jumbo RX DMA tag\n");
+ return (error);
+ }
+ }
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);
@@ -1083,6 +1110,24 @@ re_allocmem(device_t dev, struct rl_softc *sc)
/* Create DMA maps for RX buffers */
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
+ error = bus_dmamap_create(sc->rl_ldata.rl_jrx_mtag, 0,
+ &sc->rl_ldata.rl_jrx_sparemap);
+ if (error) {
+ device_printf(dev,
+ "could not create spare DMA map for jumbo RX\n");
+ return (error);
+ }
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ error = bus_dmamap_create(sc->rl_ldata.rl_jrx_mtag, 0,
+ &sc->rl_ldata.rl_jrx_desc[i].rx_dmamap);
+ if (error) {
+ device_printf(dev,
+ "could not create DMA map for jumbo RX\n");
+ return (error);
+ }
+ }
+ }
error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0,
&sc->rl_ldata.rl_rx_sparemap);
if (error) {
@@ -1141,11 +1186,12 @@ re_attach(device_t dev)
u_int16_t as[ETHER_ADDR_LEN / 2];
struct rl_softc *sc;
struct ifnet *ifp;
- struct rl_hwrev *hw_rev;
+ const struct rl_hwrev *hw_rev;
+ u_int32_t cap, ctl;
int hwrev;
u_int16_t devid, re_did = 0;
int error = 0, i, phy, rid;
- int msic, reg;
+ int msic, msixc, reg;
uint8_t cfg;
sc = device_get_softc(dev);
@@ -1195,23 +1241,53 @@ re_attach(device_t dev)
sc->rl_btag = rman_get_bustag(sc->rl_res);
sc->rl_bhandle = rman_get_bushandle(sc->rl_res);
- msic = 0;
+ msic = pci_msi_count(dev);
+ msixc = pci_msix_count(dev);
if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
sc->rl_flags |= RL_FLAG_PCIE;
- if (devid != RT_DEVICEID_8101E) {
- /* Set PCIe maximum read request size to 2048. */
- if (pci_get_max_read_req(dev) < 2048)
- pci_set_max_read_req(dev, 2048);
+ sc->rl_expcap = reg;
+ }
+ if (bootverbose) {
+ device_printf(dev, "MSI count : %d\n", msic);
+ device_printf(dev, "MSI-X count : %d\n", msixc);
+ }
+ if (msix_disable > 0)
+ msixc = 0;
+ if (msi_disable > 0)
+ msic = 0;
+ /* Prefer MSI-X to MSI. */
+ if (msixc > 0) {
+ msixc = 1;
+ rid = PCIR_BAR(4);
+ sc->rl_res_pba = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->rl_res_pba == NULL) {
+ device_printf(sc->rl_dev,
+ "could not allocate MSI-X PBA resource\n");
+ }
+ if (sc->rl_res_pba != NULL &&
+ pci_alloc_msix(dev, &msixc) == 0) {
+ if (msixc == 1) {
+ device_printf(dev, "Using %d MSI-X message\n",
+ msixc);
+ sc->rl_flags |= RL_FLAG_MSIX;
+ } else
+ pci_release_msi(dev);
+ }
+ if ((sc->rl_flags & RL_FLAG_MSIX) == 0) {
+ if (sc->rl_res_pba != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, rid,
+ sc->rl_res_pba);
+ sc->rl_res_pba = NULL;
+ msixc = 0;
}
- msic = pci_msi_count(dev);
- if (bootverbose)
- device_printf(dev, "MSI count : %d\n", msic);
}
- if (msic > 0 && msi_disable == 0) {
+ /* Prefer MSI to INTx. */
+ if (msixc == 0 && msic > 0) {
msic = 1;
if (pci_alloc_msi(dev, &msic) == 0) {
if (msic == RL_MSI_MESSAGES) {
- device_printf(dev, "Using %d MSI messages\n",
+ device_printf(dev, "Using %d MSI message\n",
msic);
sc->rl_flags |= RL_FLAG_MSI;
/* Explicitly set MSI enable bit. */
@@ -1223,10 +1299,12 @@ re_attach(device_t dev)
} else
pci_release_msi(dev);
}
+ if ((sc->rl_flags & RL_FLAG_MSI) == 0)
+ msic = 0;
}
/* Allocate interrupt */
- if ((sc->rl_flags & RL_FLAG_MSI) == 0) {
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) {
rid = 0;
sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
@@ -1260,10 +1338,22 @@ re_attach(device_t dev)
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
}
- /* Reset the adapter. */
- RL_LOCK(sc);
- re_reset(sc);
- RL_UNLOCK(sc);
+ /* Disable ASPM L0S/L1. */
+ if (sc->rl_expcap != 0) {
+ cap = pci_read_config(dev, sc->rl_expcap +
+ PCIER_LINK_CAP, 2);
+ if ((cap & PCIEM_LINK_CAP_ASPM) != 0) {
+ ctl = pci_read_config(dev, sc->rl_expcap +
+ PCIER_LINK_CTL, 2);
+ if ((ctl & 0x0003) != 0) {
+ ctl &= ~0x0003;
+ pci_write_config(dev, sc->rl_expcap +
+ PCIER_LINK_CTL, ctl, 2);
+ device_printf(dev, "ASPM disabled\n");
+ }
+ } else
+ device_printf(dev, "no ASPM capability\n");
+ }
hw_rev = re_hwrevs;
hwrev = CSR_READ_4(sc, RL_TXCFG);
@@ -1282,7 +1372,7 @@ re_attach(device_t dev)
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;
+ sc->rl_hwrev = hw_rev;
break;
}
hw_rev++;
@@ -1295,32 +1385,42 @@ re_attach(device_t dev)
switch (hw_rev->rl_rev) {
case RL_HWREV_8139CPLUS:
- sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_FASTETHER |
- RL_FLAG_AUTOPAD;
+ sc->rl_flags |= 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;
+ sc->rl_flags |= 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 |
+ sc->rl_flags |= 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_8103E:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 |
+ RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP |
+ RL_FLAG_AUTOPAD | RL_FLAG_MACSLEEP;
+ break;
+ case RL_HWREV_8401E:
+ case RL_HWREV_8105E:
+ case RL_HWREV_8105E_SPIN1:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD;
break;
- case RL_HWREV_8103E:
- sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE |
+ case RL_HWREV_8402:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD |
- RL_FLAG_MACSLEEP;
+ RL_FLAG_CMDSTOP_WAIT_TXQ;
break;
- case RL_HWREV_8168_SPIN1:
- case RL_HWREV_8168_SPIN2:
+ case RL_HWREV_8168B_SPIN1:
+ case RL_HWREV_8168B_SPIN2:
sc->rl_flags |= RL_FLAG_WOLRXENB;
/* FALLTHROUGH */
- case RL_HWREV_8168_SPIN3:
+ case RL_HWREV_8168B_SPIN3:
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_MACSTAT;
break;
case RL_HWREV_8168C_SPIN2:
@@ -1331,27 +1431,34 @@ re_attach(device_t dev)
sc->rl_flags |= RL_FLAG_MACSLEEP;
/* FALLTHROUGH */
case RL_HWREV_8168CP:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
+ RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
+ RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 | RL_FLAG_WOL_MANLINK;
+ break;
case RL_HWREV_8168D:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
+ RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
+ RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
+ RL_FLAG_WOL_MANLINK;
+ break;
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;
+ RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_AUTOPAD |
+ RL_FLAG_JUMBOV2 | RL_FLAG_WAIT_TXPOLL | RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8168E:
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
- RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_NOJUMBO;
+ RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
+ RL_FLAG_WOL_MANLINK;
+ break;
+ case RL_HWREV_8168E_VL:
+ case RL_HWREV_8168F:
+ case RL_HWREV_8411:
+ sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
+ RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
+ RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
+ RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8169_8110SB:
case RL_HWREV_8169_8110SBL:
@@ -1368,14 +1475,35 @@ re_attach(device_t dev)
break;
}
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8139CPLUS) {
+ sc->rl_cfg0 = RL_8139_CFG0;
+ sc->rl_cfg1 = RL_8139_CFG1;
+ sc->rl_cfg2 = 0;
+ sc->rl_cfg3 = RL_8139_CFG3;
+ sc->rl_cfg4 = RL_8139_CFG4;
+ sc->rl_cfg5 = RL_8139_CFG5;
+ } else {
+ sc->rl_cfg0 = RL_CFG0;
+ sc->rl_cfg1 = RL_CFG1;
+ sc->rl_cfg2 = RL_CFG2;
+ sc->rl_cfg3 = RL_CFG3;
+ sc->rl_cfg4 = RL_CFG4;
+ sc->rl_cfg5 = RL_CFG5;
+ }
+
+ /* Reset the adapter. */
+ RL_LOCK(sc);
+ re_reset(sc);
+ RL_UNLOCK(sc);
+
/* Enable PME. */
CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
- cfg = CSR_READ_1(sc, RL_CFG1);
+ cfg = CSR_READ_1(sc, sc->rl_cfg1);
cfg |= RL_CFG1_PME;
- CSR_WRITE_1(sc, RL_CFG1, cfg);
- cfg = CSR_READ_1(sc, RL_CFG5);
+ CSR_WRITE_1(sc, sc->rl_cfg1, cfg);
+ cfg = CSR_READ_1(sc, sc->rl_cfg5);
cfg &= RL_CFG5_PME_STS;
- CSR_WRITE_1(sc, RL_CFG5, cfg);
+ CSR_WRITE_1(sc, sc->rl_cfg5, cfg);
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
if ((sc->rl_flags & RL_FLAG_PAR) != 0) {
@@ -1397,7 +1525,7 @@ re_attach(device_t dev)
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));
+ bcopy(as, eaddr, ETHER_ADDR_LEN);
}
if (sc->rl_type == RL_8169) {
@@ -1437,50 +1565,51 @@ re_attach(device_t dev)
}
/* Take PHY out of power down mode. */
- if ((sc->rl_flags & RL_FLAG_PHYWAKE_PM) != 0)
+ if ((sc->rl_flags & RL_FLAG_PHYWAKE_PM) != 0) {
CSR_WRITE_1(sc, RL_PMCH, CSR_READ_1(sc, RL_PMCH) | 0x80);
+ if (hw_rev->rl_rev == RL_HWREV_8401E)
+ CSR_WRITE_1(sc, 0xD1, CSR_READ_1(sc, 0xD1) & ~0x08);
+ }
if ((sc->rl_flags & RL_FLAG_PHYWAKE) != 0) {
re_gmii_writereg(dev, 1, 0x1f, 0);
re_gmii_writereg(dev, 1, 0x0e, 0);
}
-#define RE_PHYAD_INTERNAL 0
-
- /* Do MII setup. */
- phy = RE_PHYAD_INTERNAL;
- if (sc->rl_type == RL_8169)
- phy = 1;
- error = mii_attach(dev, &sc->rl_miibus, ifp, re_ifmedia_upd,
- re_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, MIIF_DOPAUSE);
- if (error != 0) {
- device_printf(dev, "attaching PHYs failed\n");
- goto fail;
- }
-
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = re_ioctl;
ifp->if_start = re_start;
- ifp->if_hwassist = RE_CSUM_FEATURES;
- ifp->if_capabilities = IFCAP_HWCSUM;
+ /*
+ * RTL8168/8111C generates wrong IP checksummed frame if the
+ * packet has IP options so disable TX IP checksum offloading.
+ */
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8168C ||
+ sc->rl_hwrev->rl_rev == RL_HWREV_8168C_SPIN2)
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+ else
+ ifp->if_hwassist = CSUM_IP | CSUM_TCP | CSUM_UDP;
+ ifp->if_hwassist |= CSUM_TSO;
+ ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_TSO4;
ifp->if_capenable = ifp->if_capabilities;
ifp->if_init = re_init;
IFQ_SET_MAXLEN(&ifp->if_snd, RL_IFQ_MAXLEN);
ifp->if_snd.ifq_drv_maxlen = RL_IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- TASK_INIT(&sc->rl_txtask, 1, re_tx_task, ifp);
TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc);
- /*
- * XXX
- * Still have no idea how to make TSO work on 8168C, 8168CP,
- * 8111C and 8111CP.
- */
- if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
- ifp->if_hwassist |= CSUM_TSO;
- ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_VLAN_HWTSO;
+#define RE_PHYAD_INTERNAL 0
+
+ /* Do MII setup. */
+ phy = RE_PHYAD_INTERNAL;
+ if (sc->rl_type == RL_8169)
+ phy = 1;
+ error = mii_attach(dev, &sc->rl_miibus, ifp, re_ifmedia_upd,
+ re_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, MIIF_DOPAUSE);
+ if (error != 0) {
+ device_printf(dev, "attaching PHYs failed\n");
+ goto fail;
}
/*
@@ -1496,10 +1625,11 @@ re_attach(device_t dev)
if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &reg) == 0)
ifp->if_capabilities |= IFCAP_WOL;
ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_capenable &= ~(IFCAP_WOL_UCAST | IFCAP_WOL_MCAST);
/*
- * Don't enable TSO by default. Under certain
- * circumtances the controller generated corrupted
- * packets in TSO size.
+ * Don't enable TSO by default. It is known to generate
+ * corrupted TCP segments(bad TCP options) under certain
+ * circumtances.
*/
ifp->if_hwassist &= ~CSUM_TSO;
ifp->if_capenable &= ~(IFCAP_TSO4 | IFCAP_VLAN_HWTSO);
@@ -1531,19 +1661,19 @@ re_attach(device_t dev)
}
#endif
+#ifdef RE_TX_MODERATION
+ intr_filter = 1;
+#endif
/* Hook interrupt last to avoid having to lock softc */
- if ((sc->rl_flags & RL_FLAG_MSI) == 0)
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 &&
+ intr_filter == 0) {
+ error = bus_setup_intr(dev, sc->rl_irq[0],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, re_intr_msi, sc,
+ &sc->rl_intrhand[0]);
+ } else {
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");
@@ -1590,7 +1720,6 @@ re_detach(device_t dev)
RL_UNLOCK(sc);
callout_drain(&sc->rl_stat_callout);
taskqueue_drain(taskqueue_fast, &sc->rl_inttask);
- taskqueue_drain(taskqueue_fast, &sc->rl_txtask);
/*
* Force off the IFF_UP flag here, in case someone
* still had a BPF descriptor attached to this
@@ -1615,30 +1744,25 @@ re_detach(device_t dev)
* stopped here.
*/
- 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 (sc->rl_intrhand[0] != NULL) {
+ bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]);
+ sc->rl_intrhand[0] = NULL;
}
if (ifp != NULL)
if_free(ifp);
- 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;
- }
- }
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0)
+ rid = 0;
+ else
+ rid = 1;
+ if (sc->rl_irq[0] != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->rl_irq[0]);
+ sc->rl_irq[0] = NULL;
+ }
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0)
pci_release_msi(dev);
+ if (sc->rl_res_pba) {
+ rid = PCIR_BAR(4);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->rl_res_pba);
}
if (sc->rl_res)
bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id,
@@ -1673,21 +1797,35 @@ re_detach(device_t dev)
/* Destroy all the RX and TX buffer maps */
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);
+ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
+ if (sc->rl_ldata.rl_tx_desc[i].tx_dmamap)
+ 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);
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ if (sc->rl_ldata.rl_rx_desc[i].rx_dmamap)
+ 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);
}
-
+ if (sc->rl_ldata.rl_jrx_mtag) {
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ if (sc->rl_ldata.rl_jrx_desc[i].rx_dmamap)
+ bus_dmamap_destroy(sc->rl_ldata.rl_jrx_mtag,
+ sc->rl_ldata.rl_jrx_desc[i].rx_dmamap);
+ }
+ if (sc->rl_ldata.rl_jrx_sparemap)
+ bus_dmamap_destroy(sc->rl_ldata.rl_jrx_mtag,
+ sc->rl_ldata.rl_jrx_sparemap);
+ bus_dma_tag_destroy(sc->rl_ldata.rl_jrx_mtag);
+ }
/* Unload and free the stats buffer and map */
if (sc->rl_ldata.rl_stag) {
@@ -1715,7 +1853,11 @@ re_discard_rxbuf(struct rl_softc *sc, int idx)
struct rl_rxdesc *rxd;
uint32_t cmdstat;
- rxd = &sc->rl_ldata.rl_rx_desc[idx];
+ if (sc->rl_ifp->if_mtu > RL_MTU &&
+ (sc->rl_flags & RL_FLAG_JUMBOV2) != 0)
+ rxd = &sc->rl_ldata.rl_jrx_desc[idx];
+ else
+ rxd = &sc->rl_ldata.rl_rx_desc[idx];
desc = &sc->rl_ldata.rl_rx_list[idx];
desc->rl_vlanctl = 0;
cmdstat = rxd->rx_size;
@@ -1788,6 +1930,59 @@ re_newbuf(struct rl_softc *sc, int idx)
return (0);
}
+static int
+re_jumbo_newbuf(struct rl_softc *sc, int idx)
+{
+ 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;
+
+ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUM9BYTES);
+ if (m == NULL)
+ return (ENOBUFS);
+ m->m_len = m->m_pkthdr.len = MJUM9BYTES;
+#ifdef RE_FIXUP_RX
+ m_adj(m, RE_ETHER_ALIGN);
+#endif
+ error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_jrx_mtag,
+ sc->rl_ldata.rl_jrx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs));
+
+ rxd = &sc->rl_ldata.rl_jrx_desc[idx];
+ if (rxd->rx_m != NULL) {
+ bus_dmamap_sync(sc->rl_ldata.rl_jrx_mtag, rxd->rx_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rl_ldata.rl_jrx_mtag, rxd->rx_dmamap);
+ }
+
+ rxd->rx_m = m;
+ map = rxd->rx_dmamap;
+ rxd->rx_dmamap = sc->rl_ldata.rl_jrx_sparemap;
+ rxd->rx_size = segs[0].ds_len;
+ sc->rl_ldata.rl_jrx_sparemap = map;
+ bus_dmamap_sync(sc->rl_ldata.rl_jrx_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(struct mbuf *m)
@@ -1853,6 +2048,31 @@ re_rx_list_init(struct rl_softc *sc)
sc->rl_ldata.rl_rx_prodidx = 0;
sc->rl_head = sc->rl_tail = NULL;
+ sc->rl_int_rx_act = 0;
+
+ return (0);
+}
+
+static int
+re_jrx_list_init(struct rl_softc *sc)
+{
+ int error, i;
+
+ 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_jrx_desc[i].rx_m = NULL;
+ if ((error = re_jumbo_newbuf(sc, i)) != 0)
+ return (error);
+ }
+
+ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
+ sc->rl_ldata.rl_rx_list_map,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ sc->rl_ldata.rl_rx_prodidx = 0;
+ sc->rl_head = sc->rl_tail = NULL;
+ sc->rl_int_rx_act = 0;
return (0);
}
@@ -1867,14 +2087,18 @@ re_rxeof(struct rl_softc *sc, int *rx_npktsp)
{
struct mbuf *m;
struct ifnet *ifp;
- int i, total_len;
+ int i, rxerr, total_len;
struct rl_desc *cur_rx;
u_int32_t rxstat, rxvlan;
- int maxpkt = 16, rx_npkts = 0;
+ int jumbo, maxpkt = 16, rx_npkts = 0;
RL_LOCK_ASSERT(sc);
ifp = sc->rl_ifp;
+ if (ifp->if_mtu > RL_MTU && (sc->rl_flags & RL_FLAG_JUMBOV2) != 0)
+ jumbo = 1;
+ else
+ jumbo = 0;
/* Invalidate the descriptor memory */
@@ -1892,9 +2116,21 @@ re_rxeof(struct rl_softc *sc, int *rx_npktsp)
break;
total_len = rxstat & sc->rl_rxlenmask;
rxvlan = le32toh(cur_rx->rl_vlanctl);
- m = sc->rl_ldata.rl_rx_desc[i].rx_m;
+ if (jumbo != 0)
+ m = sc->rl_ldata.rl_jrx_desc[i].rx_m;
+ else
+ m = sc->rl_ldata.rl_rx_desc[i].rx_m;
- if (!(rxstat & RL_RDESC_STAT_EOF)) {
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
+ (rxstat & (RL_RDESC_STAT_SOF | RL_RDESC_STAT_EOF)) !=
+ (RL_RDESC_STAT_SOF | RL_RDESC_STAT_EOF)) {
+ /*
+ * RTL8168C or later controllers do not
+ * support multi-fragment packet.
+ */
+ re_discard_rxbuf(sc, i);
+ continue;
+ } else if ((rxstat & RL_RDESC_STAT_EOF) == 0) {
if (re_newbuf(sc, i) != 0) {
/*
* If this is part of a multi-fragment packet,
@@ -1941,27 +2177,36 @@ re_rxeof(struct rl_softc *sc, int *rx_npktsp)
* if total_len > 2^13-1, both _RXERRSUM and _GIANT will be
* set, but if CRC is clear, it will still be a valid frame.
*/
- if (rxstat & RL_RDESC_STAT_RXERRSUM && !(total_len > 8191 &&
- (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)) {
- ifp->if_ierrors++;
- /*
- * 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;
+ if ((rxstat & RL_RDESC_STAT_RXERRSUM) != 0) {
+ rxerr = 1;
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) == 0 &&
+ total_len > 8191 &&
+ (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)
+ rxerr = 0;
+ if (rxerr != 0) {
+ ifp->if_ierrors++;
+ /*
+ * 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;
}
- re_discard_rxbuf(sc, i);
- continue;
}
/*
* If allocating a replacement mbuf fails,
* reload the current one.
*/
-
- if (re_newbuf(sc, i) != 0) {
+ if (jumbo != 0)
+ rxerr = re_jumbo_newbuf(sc, i);
+ else
+ rxerr = re_newbuf(sc, i);
+ if (rxerr != 0) {
ifp->if_iqdrops++;
if (sc->rl_head != NULL) {
m_freem(sc->rl_head);
@@ -1972,9 +2217,13 @@ re_rxeof(struct rl_softc *sc, int *rx_npktsp)
}
if (sc->rl_head != NULL) {
- m->m_len = total_len % RE_RX_DESC_BUFLEN;
- if (m->m_len == 0)
- m->m_len = RE_RX_DESC_BUFLEN;
+ if (jumbo != 0)
+ m->m_len = total_len;
+ else {
+ m->m_len = total_len % RE_RX_DESC_BUFLEN;
+ if (m->m_len == 0)
+ m->m_len = RE_RX_DESC_BUFLEN;
+ }
/*
* Special case: if there's 4 bytes or less
* in this buffer, the mbuf can be discarded:
@@ -2194,7 +2443,7 @@ re_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
re_txeof(sc);
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
+ re_start_locked(ifp);
if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
u_int16_t status;
@@ -2297,7 +2546,7 @@ re_int_task(void *arg, int npending)
}
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
+ re_start_locked(ifp);
RL_UNLOCK(sc);
@@ -2309,6 +2558,87 @@ re_int_task(void *arg, int npending)
CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS);
}
+static void
+re_intr_msi(void *xsc)
+{
+ struct rl_softc *sc;
+ struct ifnet *ifp;
+ uint16_t intrs, status;
+
+ sc = xsc;
+ RL_LOCK(sc);
+
+ ifp = sc->rl_ifp;
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING) {
+ RL_UNLOCK(sc);
+ return;
+ }
+#endif
+ /* Disable interrupts. */
+ CSR_WRITE_2(sc, RL_IMR, 0);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ RL_UNLOCK(sc);
+ return;
+ }
+
+ intrs = RL_INTRS_CPLUS;
+ status = CSR_READ_2(sc, RL_ISR);
+ CSR_WRITE_2(sc, RL_ISR, status);
+ if (sc->rl_int_rx_act > 0) {
+ intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW |
+ RL_ISR_RX_OVERRUN);
+ status &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW |
+ RL_ISR_RX_OVERRUN);
+ }
+
+ if (status & (RL_ISR_TIMEOUT_EXPIRED | RL_ISR_RX_OK | RL_ISR_RX_ERR |
+ RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) {
+ re_rxeof(sc, NULL);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ if (sc->rl_int_rx_mod != 0 &&
+ (status & (RL_ISR_RX_OK | RL_ISR_RX_ERR |
+ RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) != 0) {
+ /* Rearm one-shot timer. */
+ CSR_WRITE_4(sc, RL_TIMERCNT, 1);
+ intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR |
+ RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN);
+ sc->rl_int_rx_act = 1;
+ } else {
+ intrs |= RL_ISR_RX_OK | RL_ISR_RX_ERR |
+ RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN;
+ sc->rl_int_rx_act = 0;
+ }
+ }
+ }
+
+ /*
+ * 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 & (RL_ISR_TX_OK | RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL))
+ re_txeof(sc);
+
+ if (status & RL_ISR_SYSTEM_ERR) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ re_init_locked(sc);
+ }
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ re_start_locked(ifp);
+ CSR_WRITE_2(sc, RL_IMR, intrs);
+ }
+ RL_UNLOCK(sc);
+}
+
static int
re_encap(struct rl_softc *sc, struct mbuf **m_head)
{
@@ -2415,11 +2745,17 @@ re_encap(struct rl_softc *sc, struct mbuf **m_head)
*/
vlanctl = 0;
csum_flags = 0;
- 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 {
+ if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
+ if ((sc->rl_flags & RL_FLAG_DESCV2) != 0) {
+ csum_flags |= RL_TDESC_CMD_LGSEND;
+ vlanctl |= ((uint32_t)(*m_head)->m_pkthdr.tso_segsz <<
+ RL_TDESC_CMD_MSSVALV2_SHIFT);
+ } else {
+ 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
@@ -2496,19 +2832,21 @@ re_encap(struct rl_softc *sc, struct mbuf **m_head)
}
static void
-re_tx_task(void *arg, int npending)
+re_start(struct ifnet *ifp)
{
- struct ifnet *ifp;
+ struct rl_softc *sc;
- ifp = arg;
- re_start(ifp);
+ sc = ifp->if_softc;
+ RL_LOCK(sc);
+ re_start_locked(ifp);
+ RL_UNLOCK(sc);
}
/*
* Main transmit routine for C+ and gigE NICs.
*/
static void
-re_start(struct ifnet *ifp)
+re_start_locked(struct ifnet *ifp)
{
struct rl_softc *sc;
struct mbuf *m_head;
@@ -2516,13 +2854,9 @@ re_start(struct ifnet *ifp)
sc = ifp->if_softc;
- RL_LOCK(sc);
-
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
- IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) {
- RL_UNLOCK(sc);
+ IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0)
return;
- }
for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
sc->rl_ldata.rl_tx_free > 1;) {
@@ -2552,7 +2886,6 @@ re_start(struct ifnet *ifp)
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);
return;
}
@@ -2580,8 +2913,59 @@ re_start(struct ifnet *ifp)
* Set a timeout in case the chip goes out to lunch.
*/
sc->rl_watchdog_timer = 5;
+}
- RL_UNLOCK(sc);
+static void
+re_set_jumbo(struct rl_softc *sc, int jumbo)
+{
+
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8168E_VL) {
+ pci_set_max_read_req(sc->rl_dev, 4096);
+ return;
+ }
+
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
+ if (jumbo != 0) {
+ CSR_WRITE_1(sc, sc->rl_cfg3, CSR_READ_1(sc, sc->rl_cfg3) |
+ RL_CFG3_JUMBO_EN0);
+ switch (sc->rl_hwrev->rl_rev) {
+ case RL_HWREV_8168DP:
+ break;
+ case RL_HWREV_8168E:
+ CSR_WRITE_1(sc, sc->rl_cfg4,
+ CSR_READ_1(sc, sc->rl_cfg4) | 0x01);
+ break;
+ default:
+ CSR_WRITE_1(sc, sc->rl_cfg4,
+ CSR_READ_1(sc, sc->rl_cfg4) | RL_CFG4_JUMBO_EN1);
+ }
+ } else {
+ CSR_WRITE_1(sc, sc->rl_cfg3, CSR_READ_1(sc, sc->rl_cfg3) &
+ ~RL_CFG3_JUMBO_EN0);
+ switch (sc->rl_hwrev->rl_rev) {
+ case RL_HWREV_8168DP:
+ break;
+ case RL_HWREV_8168E:
+ CSR_WRITE_1(sc, sc->rl_cfg4,
+ CSR_READ_1(sc, sc->rl_cfg4) & ~0x01);
+ break;
+ default:
+ CSR_WRITE_1(sc, sc->rl_cfg4,
+ CSR_READ_1(sc, sc->rl_cfg4) & ~RL_CFG4_JUMBO_EN1);
+ }
+ }
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ switch (sc->rl_hwrev->rl_rev) {
+ case RL_HWREV_8168DP:
+ pci_set_max_read_req(sc->rl_dev, 4096);
+ break;
+ default:
+ if (jumbo != 0)
+ pci_set_max_read_req(sc->rl_dev, 512);
+ else
+ pci_set_max_read_req(sc->rl_dev, 4096);
+ }
}
static void
@@ -2622,6 +3006,45 @@ re_init_locked(struct rl_softc *sc)
re_reset(sc);
/*
+ * For C+ mode, initialize the RX descriptors and mbufs.
+ */
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
+ if (ifp->if_mtu > RL_MTU) {
+ if (re_jrx_list_init(sc) != 0) {
+ device_printf(sc->rl_dev,
+ "no memory for jumbo RX buffers\n");
+ re_stop(sc);
+ return;
+ }
+ /* Disable checksum offloading for jumbo frames. */
+ ifp->if_capenable &= ~(IFCAP_HWCSUM | IFCAP_TSO4);
+ ifp->if_hwassist &= ~(RE_CSUM_FEATURES | CSUM_TSO);
+ } else {
+ if (re_rx_list_init(sc) != 0) {
+ device_printf(sc->rl_dev,
+ "no memory for RX buffers\n");
+ re_stop(sc);
+ return;
+ }
+ }
+ re_set_jumbo(sc, ifp->if_mtu > RL_MTU);
+ } else {
+ if (re_rx_list_init(sc) != 0) {
+ device_printf(sc->rl_dev, "no memory for RX buffers\n");
+ re_stop(sc);
+ return;
+ }
+ if ((sc->rl_flags & RL_FLAG_PCIE) != 0 &&
+ pci_get_device(sc->rl_dev) != RT_DEVICEID_8101E) {
+ if (ifp->if_mtu > RL_MTU)
+ pci_set_max_read_req(sc->rl_dev, 512);
+ else
+ pci_set_max_read_req(sc->rl_dev, 4096);
+ }
+ }
+ re_tx_list_init(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.
@@ -2638,12 +3061,12 @@ re_init_locked(struct rl_softc *sc)
} else
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) {
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SC ||
+ sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SCE) {
reg = 0x000fff00;
- if ((CSR_READ_1(sc, RL_CFG2) & RL_CFG2_PCI66MHZ) != 0)
+ if ((CSR_READ_1(sc, sc->rl_cfg2) & RL_CFG2_PCI66MHZ) != 0)
reg |= 0x000000ff;
- if (sc->rl_hwrev == RL_HWREV_8169_8110SCE)
+ if (sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SCE)
reg |= 0x00f00000;
CSR_WRITE_4(sc, 0x7c, reg);
/* Disable interrupt mitigation. */
@@ -2673,12 +3096,6 @@ re_init_locked(struct rl_softc *sc)
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
/*
- * For C+ mode, initialize the RX descriptors and mbufs.
- */
- re_rx_list_init(sc);
- re_tx_list_init(sc);
-
- /*
* Load the addresses of the RX and TX lists into the chip.
*/
@@ -2719,20 +3136,8 @@ re_init_locked(struct rl_softc *sc)
/* Configure interrupt moderation. */
if (sc->rl_type == RL_8169) {
- switch (sc->rl_hwrev) {
- case RL_HWREV_8100E:
- case RL_HWREV_8101E:
- case RL_HWREV_8102E:
- case RL_HWREV_8102EL:
- case RL_HWREV_8102EL_SPIN1:
- case RL_HWREV_8103E:
- CSR_WRITE_2(sc, RL_INTRMOD, 0);
- break;
- default:
- /* Magic from vendor. */
- CSR_WRITE_2(sc, RL_INTRMOD, 0x5100);
- break;
- }
+ /* Magic from vendor. */
+ CSR_WRITE_2(sc, RL_INTRMOD, 0x5100);
}
#ifdef DEVICE_POLLING
@@ -2763,18 +3168,35 @@ re_init_locked(struct rl_softc *sc)
CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);
#endif
-#ifdef RE_TX_MODERATION
/*
* Initialize the timer interrupt register so that
* a timer interrupt will be generated once the timer
* reaches a certain number of ticks. The timer is
- * reloaded on each transmit. This gives us TX interrupt
+ * reloaded on each transmit.
+ */
+#ifdef RE_TX_MODERATION
+ /*
+ * Use timer interrupt register to moderate TX interrupt
* moderation, which dramatically improves TX frame rate.
*/
if (sc->rl_type == RL_8169)
CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800);
else
CSR_WRITE_4(sc, RL_TIMERINT, 0x400);
+#else
+ /*
+ * Use timer interrupt register to moderate RX interrupt
+ * moderation.
+ */
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 &&
+ intr_filter == 0) {
+ if (sc->rl_type == RL_8169)
+ CSR_WRITE_4(sc, RL_TIMERINT_8169,
+ RL_USECS(sc->rl_int_rx_mod));
+ } else {
+ if (sc->rl_type == RL_8169)
+ CSR_WRITE_4(sc, RL_TIMERINT_8169, RL_USECS(0));
+ }
#endif
/*
@@ -2782,24 +3204,40 @@ re_init_locked(struct rl_softc *sc)
* size so we can receive jumbo frames.
*/
if (sc->rl_type == RL_8169) {
- if ((sc->rl_flags & (RL_FLAG_PCIE | RL_FLAG_NOJUMBO)) ==
- (RL_FLAG_PCIE | RL_FLAG_NOJUMBO))
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
+ /*
+ * For controllers that use new jumbo frame scheme,
+ * set maximum size of jumbo frame depedning on
+ * controller revisions.
+ */
+ if (ifp->if_mtu > RL_MTU)
+ CSR_WRITE_2(sc, RL_MAXRXPKTLEN,
+ sc->rl_hwrev->rl_max_mtu +
+ ETHER_VLAN_ENCAP_LEN + ETHER_HDR_LEN +
+ ETHER_CRC_LEN);
+ else
+ CSR_WRITE_2(sc, RL_MAXRXPKTLEN,
+ RE_RX_DESC_BUFLEN);
+ } else if ((sc->rl_flags & RL_FLAG_PCIE) != 0 &&
+ sc->rl_hwrev->rl_max_mtu == RL_MTU) {
+ /* RTL810x has no jumbo frame support. */
CSR_WRITE_2(sc, RL_MAXRXPKTLEN, RE_RX_DESC_BUFLEN);
- else
+ } else
CSR_WRITE_2(sc, RL_MAXRXPKTLEN, 16383);
}
if (sc->rl_testmode)
return;
- mii_mediachg(mii);
-
- CSR_WRITE_1(sc, RL_CFG1, CSR_READ_1(sc, RL_CFG1) | RL_CFG1_DRVLOAD);
+ CSR_WRITE_1(sc, sc->rl_cfg1, CSR_READ_1(sc, sc->rl_cfg1) |
+ RL_CFG1_DRVLOAD);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
sc->rl_flags &= ~RL_FLAG_LINK;
+ mii_mediachg(mii);
+
sc->rl_watchdog_timer = 0;
callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
}
@@ -2837,9 +3275,9 @@ re_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
RL_LOCK(sc);
mii_pollstat(mii);
- RL_UNLOCK(sc);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
+ RL_UNLOCK(sc);
}
static int
@@ -2848,26 +3286,30 @@ re_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
struct rl_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *) data;
struct mii_data *mii;
+ uint32_t rev;
int error = 0;
switch (command) {
case SIOCSIFMTU:
- 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) {
+ if (ifr->ifr_mtu < ETHERMIN ||
+ ifr->ifr_mtu > sc->rl_hwrev->rl_max_mtu) {
error = EINVAL;
break;
}
RL_LOCK(sc);
- if (ifp->if_mtu != ifr->ifr_mtu)
+ if (ifp->if_mtu != ifr->ifr_mtu) {
ifp->if_mtu = ifr->ifr_mtu;
- if (ifp->if_mtu > RL_TSO_MTU &&
- (ifp->if_capenable & IFCAP_TSO4) != 0) {
- ifp->if_capenable &= ~IFCAP_TSO4;
- ifp->if_hwassist &= ~CSUM_TSO;
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ re_init_locked(sc);
+ }
+ if (ifp->if_mtu > RL_TSO_MTU &&
+ (ifp->if_capenable & IFCAP_TSO4) != 0) {
+ ifp->if_capenable &= ~(IFCAP_TSO4 |
+ IFCAP_VLAN_HWTSO);
+ ifp->if_hwassist &= ~CSUM_TSO;
+ }
VLAN_CAPABILITIES(ifp);
}
RL_UNLOCK(sc);
@@ -2927,16 +3369,28 @@ re_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
}
}
#endif /* DEVICE_POLLING */
- if (mask & IFCAP_HWCSUM) {
- ifp->if_capenable ^= IFCAP_HWCSUM;
- if (ifp->if_capenable & IFCAP_TXCSUM)
- ifp->if_hwassist |= RE_CSUM_FEATURES;
- else
+ RL_LOCK(sc);
+ if ((mask & IFCAP_TXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_TXCSUM;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) {
+ rev = sc->rl_hwrev->rl_rev;
+ if (rev == RL_HWREV_8168C ||
+ rev == RL_HWREV_8168C_SPIN2)
+ ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
+ else
+ ifp->if_hwassist |= RE_CSUM_FEATURES;
+ } else
ifp->if_hwassist &= ~RE_CSUM_FEATURES;
reinit = 1;
}
+ if ((mask & IFCAP_RXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+ reinit = 1;
+ }
if ((mask & IFCAP_TSO4) != 0 &&
- (ifp->if_capabilities & IFCAP_TSO) != 0) {
+ (ifp->if_capabilities & IFCAP_TSO4) != 0) {
ifp->if_capenable ^= IFCAP_TSO4;
if ((IFCAP_TSO4 & ifp->if_capenable) != 0)
ifp->if_hwassist |= CSUM_TSO;
@@ -2959,6 +3413,10 @@ re_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
reinit = 1;
}
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
+ (mask & (IFCAP_HWCSUM | IFCAP_TSO4 |
+ IFCAP_VLAN_HWTSO)) != 0)
+ reinit = 1;
if ((mask & IFCAP_WOL) != 0 &&
(ifp->if_capabilities & IFCAP_WOL) != 0) {
if ((mask & IFCAP_WOL_UCAST) != 0)
@@ -2970,8 +3428,9 @@ re_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
}
if (reinit && ifp->if_drv_flags & IFF_DRV_RUNNING) {
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
- re_init(sc);
+ re_init_locked(sc);
}
+ RL_UNLOCK(sc);
VLAN_CAPABILITIES(ifp);
}
break;
@@ -2999,7 +3458,7 @@ re_watchdog(struct rl_softc *sc)
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);
+ re_start_locked(ifp);
return;
}
@@ -3010,7 +3469,7 @@ re_watchdog(struct rl_softc *sc)
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
re_init_locked(sc);
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask);
+ re_start_locked(ifp);
}
/*
@@ -3033,10 +3492,42 @@ re_stop(struct rl_softc *sc)
callout_stop(&sc->rl_stat_callout);
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- if ((sc->rl_flags & RL_FLAG_CMDSTOP) != 0)
+ /*
+ * Disable accepting frames to put RX MAC into idle state.
+ * Otherwise it's possible to get frames while stop command
+ * execution is in progress and controller can DMA the frame
+ * to already freed RX buffer during that period.
+ */
+ CSR_WRITE_4(sc, RL_RXCFG, CSR_READ_4(sc, RL_RXCFG) &
+ ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_MULTI |
+ RL_RXCFG_RX_BROAD));
+
+ if ((sc->rl_flags & RL_FLAG_WAIT_TXPOLL) != 0) {
+ for (i = RL_TIMEOUT; i > 0; i--) {
+ if ((CSR_READ_1(sc, sc->rl_txstart) &
+ RL_TXSTART_START) == 0)
+ break;
+ DELAY(20);
+ }
+ if (i == 0)
+ device_printf(sc->rl_dev,
+ "stopping TX poll timed out!\n");
+ CSR_WRITE_1(sc, RL_COMMAND, 0x00);
+ } else 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
+ if ((sc->rl_flags & RL_FLAG_CMDSTOP_WAIT_TXQ) != 0) {
+ for (i = RL_TIMEOUT; i > 0; i--) {
+ if ((CSR_READ_4(sc, RL_TXCFG) &
+ RL_TXCFG_QUEUE_EMPTY) != 0)
+ break;
+ DELAY(100);
+ }
+ if (i == 0)
+ device_printf(sc->rl_dev,
+ "stopping TXQ timed out!\n");
+ }
+ } else
CSR_WRITE_1(sc, RL_COMMAND, 0x00);
DELAY(1000);
CSR_WRITE_2(sc, RL_IMR, 0x0000);
@@ -3048,7 +3539,6 @@ re_stop(struct rl_softc *sc)
}
/* Free the TX list buffers. */
-
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) {
@@ -3062,11 +3552,10 @@ re_stop(struct rl_softc *sc)
}
/* Free the RX list buffers. */
-
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,
+ 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);
@@ -3074,6 +3563,20 @@ re_stop(struct rl_softc *sc)
rxd->rx_m = NULL;
}
}
+
+ if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
+ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
+ rxd = &sc->rl_ldata.rl_jrx_desc[i];
+ if (rxd->rx_m != NULL) {
+ bus_dmamap_sync(sc->rl_ldata.rl_jrx_mtag,
+ rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rl_ldata.rl_jrx_mtag,
+ rxd->rx_dmamap);
+ m_freem(rxd->rx_m);
+ rxd->rx_m = NULL;
+ }
+ }
+ }
}
/*
@@ -3162,6 +3665,74 @@ re_shutdown(device_t dev)
}
static void
+re_set_linkspeed(struct rl_softc *sc)
+{
+ struct mii_softc *miisc;
+ struct mii_data *mii;
+ int aneg, i, phyno;
+
+ RL_LOCK_ASSERT(sc);
+
+ mii = device_get_softc(sc->rl_miibus);
+ mii_pollstat(mii);
+ aneg = 0;
+ 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:
+ return;
+ case IFM_1000_T:
+ aneg++;
+ break;
+ default:
+ break;
+ }
+ }
+ miisc = LIST_FIRST(&mii->mii_phys);
+ phyno = miisc->mii_phy;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ re_miibus_writereg(sc->rl_dev, phyno, MII_100T2CR, 0);
+ re_miibus_writereg(sc->rl_dev, phyno,
+ MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA);
+ re_miibus_writereg(sc->rl_dev, phyno,
+ MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+ DELAY(1000);
+ if (aneg != 0) {
+ /*
+ * Poll link state until re(4) get a 10/100Mbps link.
+ */
+ for (i = 0; i < MII_ANEGTICKS_GIGE; i++) {
+ mii_pollstat(mii);
+ 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:
+ return;
+ default:
+ break;
+ }
+ }
+ RL_UNLOCK(sc);
+ pause("relnk", hz);
+ RL_LOCK(sc);
+ }
+ if (i == MII_ANEGTICKS_GIGE)
+ device_printf(sc->rl_dev,
+ "establishing a link failed, WOL may not work!");
+ }
+ /*
+ * No link, force MAC to have 100Mbps, full-duplex link.
+ * MAC does not require reprogramming on resolved speed/duplex,
+ * so this is just for completeness.
+ */
+ mii->mii_media_status = IFM_AVALID | IFM_ACTIVE;
+ mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
+}
+
+static void
re_setwol(struct rl_softc *sc)
{
struct ifnet *ifp;
@@ -3181,40 +3752,44 @@ re_setwol(struct rl_softc *sc)
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);
+ if ((ifp->if_capenable & IFCAP_WOL) != 0) {
+ re_set_rxmode(sc);
+ if ((sc->rl_flags & RL_FLAG_WOL_MANLINK) != 0)
+ re_set_linkspeed(sc);
+ if ((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 = CSR_READ_1(sc, sc->rl_cfg1);
v &= ~RL_CFG1_PME;
if ((ifp->if_capenable & IFCAP_WOL) != 0)
v |= RL_CFG1_PME;
- CSR_WRITE_1(sc, RL_CFG1, v);
+ CSR_WRITE_1(sc, sc->rl_cfg1, v);
- v = CSR_READ_1(sc, RL_CFG3);
+ v = CSR_READ_1(sc, sc->rl_cfg3);
v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
v |= RL_CFG3_WOL_MAGIC;
- CSR_WRITE_1(sc, RL_CFG3, v);
-
- /* Config register write done. */
- CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+ CSR_WRITE_1(sc, sc->rl_cfg3, v);
- 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;
+ v = CSR_READ_1(sc, sc->rl_cfg5);
+ v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST |
+ 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);
+ CSR_WRITE_1(sc, sc->rl_cfg5, v);
+
+ /* Config register write done. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
- if ((ifp->if_capenable & IFCAP_WOL) != 0 &&
+ if ((ifp->if_capenable & IFCAP_WOL) == 0 &&
(sc->rl_flags & RL_FLAG_PHYWAKE_PM) != 0)
CSR_WRITE_1(sc, RL_PMCH, CSR_READ_1(sc, RL_PMCH) & ~0x80);
/*
@@ -3245,17 +3820,17 @@ re_clrwol(struct rl_softc *sc)
/* Enable config register write. */
CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
- v = CSR_READ_1(sc, RL_CFG3);
+ v = CSR_READ_1(sc, sc->rl_cfg3);
v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
- CSR_WRITE_1(sc, RL_CFG3, v);
+ CSR_WRITE_1(sc, sc->rl_cfg3, v);
/* Config register write done. */
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
- v = CSR_READ_1(sc, RL_CFG5);
+ v = CSR_READ_1(sc, sc->rl_cfg5);
v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
v &= ~RL_CFG5_WOL_LANWAKE;
- CSR_WRITE_1(sc, RL_CFG5, v);
+ CSR_WRITE_1(sc, sc->rl_cfg5, v);
}
static void
@@ -3263,6 +3838,7 @@ re_add_sysctls(struct rl_softc *sc)
{
struct sysctl_ctx_list *ctx;
struct sysctl_oid_list *children;
+ int error;
ctx = device_get_sysctl_ctx(sc->rl_dev);
children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev));
@@ -3270,6 +3846,26 @@ re_add_sysctls(struct rl_softc *sc)
SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "stats",
CTLTYPE_INT | CTLFLAG_RW, sc, 0, re_sysctl_stats, "I",
"Statistics Information");
+ if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0)
+ return;
+
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "int_rx_mod",
+ CTLTYPE_INT | CTLFLAG_RW, &sc->rl_int_rx_mod, 0,
+ sysctl_hw_re_int_mod, "I", "re RX interrupt moderation");
+ /* Pull in device tunables. */
+ sc->rl_int_rx_mod = RL_TIMER_DEFAULT;
+ error = resource_int_value(device_get_name(sc->rl_dev),
+ device_get_unit(sc->rl_dev), "int_rx_mod", &sc->rl_int_rx_mod);
+ if (error == 0) {
+ if (sc->rl_int_rx_mod < RL_TIMER_MIN ||
+ sc->rl_int_rx_mod > RL_TIMER_MAX) {
+ device_printf(sc->rl_dev, "int_rx_mod value out of "
+ "range; using default: %d\n",
+ RL_TIMER_DEFAULT);
+ sc->rl_int_rx_mod = RL_TIMER_DEFAULT;
+ }
+ }
+
}
static int
@@ -3287,6 +3883,10 @@ re_sysctl_stats(SYSCTL_HANDLER_ARGS)
if (result == 1) {
sc = (struct rl_softc *)arg1;
RL_LOCK(sc);
+ if ((sc->rl_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ RL_UNLOCK(sc);
+ goto done;
+ }
bus_dmamap_sync(sc->rl_ldata.rl_stag,
sc->rl_ldata.rl_smap, BUS_DMASYNC_PREREAD);
CSR_WRITE_4(sc, RL_DUMPSTATS_HI,
@@ -3310,6 +3910,7 @@ re_sysctl_stats(SYSCTL_HANDLER_ARGS)
"DUMP statistics request timedout\n");
return (ETIMEDOUT);
}
+done:
stats = sc->rl_ldata.rl_stats;
printf("%s statistics:\n", device_get_nameunit(sc->rl_dev));
printf("Tx frames : %ju\n",
@@ -3342,3 +3943,29 @@ re_sysctl_stats(SYSCTL_HANDLER_ARGS)
return (error);
}
+
+static int
+sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
+{
+ int error, value;
+
+ if (arg1 == NULL)
+ return (EINVAL);
+ value = *(int *)arg1;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ if (value < low || value > high)
+ return (EINVAL);
+ *(int *)arg1 = value;
+
+ return (0);
+}
+
+static int
+sysctl_hw_re_int_mod(SYSCTL_HANDLER_ARGS)
+{
+
+ return (sysctl_int_range(oidp, arg1, arg2, req, RL_TIMER_MIN,
+ RL_TIMER_MAX));
+}