Module Name:    src
Committed By:   martin
Date:           Mon Sep  4 17:57:50 UTC 2023

Modified Files:
        src/sys/dev/pci [netbsd-8]: if_wm.c if_wmvar.h

Log Message:
Pull up following revision(s) (requested by msaitoh in ticket #1894):

        sys/dev/pci/if_wmvar.h: revision 1.50
        sys/dev/pci/if_wm.c: revision 1.783,1.784 via patch

Delay sending LINK_STATE_UP to prevent dropping packets on I35[04] and I21[01].

 Some (not all) systems use I35[04] or I21[01] don't send packet soon
after linkup. The MAC send a packet to the PHY and any error is not
observed. This behavior causes a problem that gratuitous ARP and/or
IPv6 DAD packet are silently dropped. To avoid this problem, don't
call mii_pollstat() here which will send LINK_STATE_UP notification
to the upper layer. Instead, mii_pollstat() will be called in
wm_gmii_mediastatus() or mii_tick() will be called in wm_tick().

Note that the similar workaround is in Linux's igb driver though it's
only for I21[01].

OK'd by hikaru@ and knakahara@.

Fix #ifdef WM_DEBUG code in wm_gmii_i82544_{read,write}reg_locked.


To generate a diff of this commit:
cvs rdiff -u -r1.508.4.50 -r1.508.4.51 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.33.6.10 -r1.33.6.11 src/sys/dev/pci/if_wmvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.508.4.50 src/sys/dev/pci/if_wm.c:1.508.4.51
--- src/sys/dev/pci/if_wm.c:1.508.4.50	Tue Jun 27 18:36:53 2023
+++ src/sys/dev/pci/if_wm.c	Mon Sep  4 17:57:49 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.508.4.50 2023/06/27 18:36:53 martin Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.508.4.51 2023/09/04 17:57:49 martin Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -82,7 +82,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.50 2023/06/27 18:36:53 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.51 2023/09/04 17:57:49 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -547,7 +547,7 @@ struct wm_softc {
 #define	WM_MEDIATYPE_COPPER		0x02
 #define	WM_MEDIATYPE_SERDES		0x03 /* Internal SERDES */
 	int sc_funcid;			/* unit number of the chip (0 to 3) */
-	int sc_flags;			/* flags; see below */
+	u_int sc_flags;			/* flags; see below */
 	int sc_if_flags;		/* last if_flags */
 	int sc_flowflags;		/* 802.3x flow control flags */
 	int sc_align_tweak;
@@ -717,6 +717,7 @@ struct wm_softc {
 	int sc_tbi_linkup;		/* TBI link status */
 	int sc_tbi_serdes_anegticks;	/* autonegotiation ticks */
 	int sc_tbi_serdes_ticks;	/* tbi ticks */
+	struct timeval sc_linkup_delay_time; /* delay LINK_STATE_UP */
 
 	int sc_mchash_type;		/* multicast filter offset */
 
@@ -3046,6 +3047,23 @@ alloc_retry:
 	    || (sc->sc_type == WM_T_I210) || (sc->sc_type == WM_T_I211))
 		sc->sc_flags |= WM_F_CRC_STRIP;
 
+	/*
+	 * Workaround for some chips to delay sending LINK_STATE_UP.
+	 * Some systems can't send packet soon after linkup. See also
+	 * wm_linkintr_gmii(), wm_tick() and wm_gmii_mediastatus().
+	 */
+	switch (sc->sc_type) {
+	case WM_T_I350:
+	case WM_T_I354:
+	case WM_T_I210:
+	case WM_T_I211:
+		if (sc->sc_mediatype == WM_MEDIATYPE_COPPER)
+			sc->sc_flags |= WM_F_DELAY_LINKUP;
+		break;
+	default:
+		break;
+	}
+
 	/* Set device properties (macflags) */
 	prop_dictionary_set_uint32(dict, "macflags", sc->sc_flags);
 
@@ -3834,9 +3852,29 @@ wm_tick(void *arg)
 
 	wm_update_stats(sc);
 
-	if (sc->sc_flags & WM_F_HAS_MII)
-		mii_tick(&sc->sc_mii);
-	else if ((sc->sc_type >= WM_T_82575) && (sc->sc_type <= WM_T_I211)
+	if (sc->sc_flags & WM_F_HAS_MII) {
+		bool dotick = true;
+
+		/*
+		 * Workaround for some chips to delay sending LINK_STATE_UP.
+		 * See also wm_linkintr_gmii() and wm_gmii_mediastatus().
+		 */
+		if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) {
+			struct timeval now;
+
+			getmicrotime(&now);
+			if (timercmp(&now, &sc->sc_linkup_delay_time, <))
+				dotick = false;
+			else if (sc->sc_linkup_delay_time.tv_sec != 0) {
+				/* Simplify by checking tv_sec only. */
+
+				sc->sc_linkup_delay_time.tv_sec = 0;
+				sc->sc_linkup_delay_time.tv_usec = 0;
+			}
+		}
+		if (dotick)
+			mii_tick(&sc->sc_mii);
+	} else if ((sc->sc_type >= WM_T_82575) && (sc->sc_type <= WM_T_I211)
 	    && (sc->sc_mediatype == WM_MEDIATYPE_SERDES))
 		wm_serdes_tick(sc);
 	else
@@ -10169,6 +10207,7 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 {
 	uint32_t status, reg;
 	bool link;
+	bool dopoll = true;
 
 	KASSERT(WM_CORE_LOCKED(sc));
 
@@ -10214,7 +10253,42 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 
 	DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n",
 		device_xname(sc->sc_dev)));
-	mii_pollstat(&sc->sc_mii);
+	if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) {
+		if (link) {
+			/* Wait 1 second. */
+			dopoll = false;
+			getmicrotime(&sc->sc_linkup_delay_time);
+			sc->sc_linkup_delay_time.tv_sec += 1;
+		} else if (sc->sc_linkup_delay_time.tv_sec != 0) {
+			/*
+			 * Simplify by checking tv_sec only. It's enough.
+			 *
+			 * Currently, it's not required to clear the time.
+			 * It's just to know the timer is stopped
+			 * (for debugging).
+			 */
+
+			sc->sc_linkup_delay_time.tv_sec = 0;
+			sc->sc_linkup_delay_time.tv_usec = 0;
+		}
+	}
+
+	/*
+	 * Call mii_pollstat().
+	 *
+	 * Some (not all) systems use I35[04] or I21[01] don't send packet soon
+	 * after linkup. The MAC send a packet to the PHY and any error is not
+	 * observed. This behavior causes a problem that gratuitous ARP and/or
+	 * IPv6 DAD packet are silently dropped. To avoid this problem, don't
+	 * call mii_pollstat() here which will send LINK_STATE_UP notification
+	 * to the upper layer. Instead, mii_pollstat() will be called in
+	 * wm_gmii_mediastatus() or mii_tick() will be called in wm_tick().
+	 */
+	if (dopoll)
+		mii_pollstat(&sc->sc_mii);
+
+	/* Do some workarounds soon after link status is changed. */
+
 	if (sc->sc_type == WM_T_82543) {
 		int miistatus, active;
 
@@ -11598,8 +11672,35 @@ static void
 wm_gmii_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 	struct wm_softc *sc = ifp->if_softc;
+	struct ethercom *ec = &sc->sc_ethercom;
+	struct mii_data *mii;
+	bool dopoll = true;
+
+	KASSERT(ec->ec_mii != NULL);
+	mii = ec->ec_mii;
+	if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) {
+		struct timeval now;
+
+		getmicrotime(&now);
+		if (timercmp(&now, &sc->sc_linkup_delay_time, <))
+			dopoll = false;
+		else if (sc->sc_linkup_delay_time.tv_sec != 0) {
+			/* Simplify by checking tv_sec only. It's enough. */
+
+			sc->sc_linkup_delay_time.tv_sec = 0;
+			sc->sc_linkup_delay_time.tv_usec = 0;
+		}
+	}
+
+	/*
+	 * Don't call mii_pollstat() while doing workaround.
+	 * See also wm_linkintr_gmii() and wm_tick().
+	 */
+	if (dopoll)
+		mii_pollstat(mii);
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
 
-	ether_mediastatus(ifp, ifmr);
 	ifmr->ifm_active = (ifmr->ifm_active & ~IFM_ETH_FMASK)
 	    | sc->sc_flowflags;
 }
@@ -11847,21 +11948,21 @@ wm_gmii_i82544_readreg_locked(device_t d
 {
 	struct wm_softc *sc = device_private(dev);
 
-	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
-		switch (sc->sc_phytype) {
-		case WMPHY_IGP:
-		case WMPHY_IGP_2:
-		case WMPHY_IGP_3:
+	switch (sc->sc_phytype) {
+	case WMPHY_IGP:
+	case WMPHY_IGP_2:
+	case WMPHY_IGP_3:
+		if (reg > BME1000_MAX_MULTI_PAGE_REG)
 			wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT,
 			    reg);
-			break;
-		default:
+		break;
+	default:
 #ifdef WM_DEBUG
+		if ((reg >> MII_ADDRBITS) != 0)
 			device_printf(dev, "%s: PHYTYPE = 0x%x, addr = %02x\n",
 			    __func__, sc->sc_phytype, reg);
 #endif
-			break;
-		}
+		break;
 	}
 
 	*val = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
@@ -11893,21 +11994,21 @@ wm_gmii_i82544_writereg_locked(device_t 
 {
 	struct wm_softc *sc = device_private(dev);
 
-	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
-		switch (sc->sc_phytype) {
-		case WMPHY_IGP:
-		case WMPHY_IGP_2:
-		case WMPHY_IGP_3:
+	switch (sc->sc_phytype) {
+	case WMPHY_IGP:
+	case WMPHY_IGP_2:
+	case WMPHY_IGP_3:
+		if (reg > BME1000_MAX_MULTI_PAGE_REG)
 			wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT,
 			    reg);
-			break;
-		default:
+		break;
+	default:
 #ifdef WM_DEBUG
+		if ((reg >> MII_ADDRBITS) != 0)
 			device_printf(dev, "%s: PHYTYPE == 0x%x, addr = %02x",
 			    __func__, sc->sc_phytype, reg);
 #endif
-			break;
-		}
+		break;
 	}
 
 	wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);

Index: src/sys/dev/pci/if_wmvar.h
diff -u src/sys/dev/pci/if_wmvar.h:1.33.6.10 src/sys/dev/pci/if_wmvar.h:1.33.6.11
--- src/sys/dev/pci/if_wmvar.h:1.33.6.10	Tue Jun 27 18:36:53 2023
+++ src/sys/dev/pci/if_wmvar.h	Mon Sep  4 17:57:49 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wmvar.h,v 1.33.6.10 2023/06/27 18:36:53 martin Exp $	*/
+/*	$NetBSD: if_wmvar.h,v 1.33.6.11 2023/09/04 17:57:49 martin Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -100,6 +100,7 @@
 #define	WM_F_SFP		0x10000000 /* SFP */
 #define	WM_F_MAS		0x20000000 /* Media Auto Sense */
 #define	WM_F_CRC_STRIP		0x40000000 /* CRC strip */
+#define	WM_F_DELAY_LINKUP	0x80000000 /* delay LINK_STATE_UP */
 
 #define WM_FLAGS "\20" \
 	"\1" "HAS_MII"	"\2" "LOCK_EECD" "\3" "_B02"	"\4" "_B03"	\
@@ -109,7 +110,7 @@
 	"\21" "NEWQUEUE" "\22" "ASF_FIRM" "\23" "ARC_SUBSYS" "\24" "AMT" \
 	"\25" "MANAGE"	"\26" "WOL"	"\27" "EEE"	"\30" "ATTACHED" \
 	"\31" "MDIC_WA"	"\32" "PCS_DIS_AUTONEGO" "\33" "PLLWA" "\34" "CLSEMWA" \
-	"\35" "SFP"	"\36" "MAS"	"\37" "CRC_STRIP"
+	"\35" "SFP"	"\36" "MAS"	"\37" "CRC_STRIP" "\40" "DELAY_LINKUP"
 
 /*
  * Variations of Intel gigabit Ethernet controller:

Reply via email to