Module Name:    src
Committed By:   martin
Date:           Mon Sep  4 17:45:24 UTC 2023

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

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

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

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.767.2.2 -r1.767.2.3 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.48.4.1 -r1.48.4.2 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.767.2.2 src/sys/dev/pci/if_wm.c:1.767.2.3
--- src/sys/dev/pci/if_wm.c:1.767.2.2	Tue Jun 27 18:24:18 2023
+++ src/sys/dev/pci/if_wm.c	Mon Sep  4 17:45:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.767.2.2 2023/06/27 18:24:18 martin Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.767.2.3 2023/09/04 17:45:24 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.767.2.2 2023/06/27 18:24:18 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.767.2.3 2023/09/04 17:45:24 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_if_wm.h"
@@ -537,7 +537,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 */
 	u_short sc_if_flags;		/* last if_flags */
 	int sc_ec_capenable;		/* last ec_capenable */
 	int sc_flowflags;		/* 802.3x flow control flags */
@@ -709,6 +709,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 */
 
@@ -3084,6 +3085,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);
 
@@ -3901,9 +3919,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
@@ -10278,6 +10316,7 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 	device_t dev = sc->sc_dev;
 	uint32_t status, reg;
 	bool link;
+	bool dopoll = true;
 	int rv;
 
 	KASSERT(mutex_owned(sc->sc_core_lock));
@@ -10324,7 +10363,46 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 
 	DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n",
 		device_xname(dev)));
-	mii_pollstat(&sc->sc_mii);
+	if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) {
+		if (link) {
+			/*
+			 * To workaround the problem, it's required to wait
+			 * several hundred miliseconds. The time depend
+			 * on the environment. Wait 1 second for the safety.
+			 */
+			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;
 
@@ -11816,10 +11894,42 @@ 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;
 
+	/*
+	 * In normal drivers, ether_mediastatus() is called here.
+	 * To avoid calling mii_pollstat(), ether_mediastatus() is open coded.
+	 */
 	KASSERT(mutex_owned(sc->sc_core_lock));
+	KASSERT(ec->ec_mii != NULL);
+	KASSERT(mii_locked(ec->ec_mii));
+
+	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;
 }
@@ -12074,23 +12184,25 @@ wm_gmii_i82544_readreg_locked(device_t d
 	struct wm_softc *sc = device_private(dev);
 	int rv;
 
-	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) {
 			rv = wm_gmii_mdic_writereg(dev, phy,
 			    IGPHY_PAGE_SELECT, reg);
 			if (rv != 0)
 				return rv;
-			break;
-		default:
+		}
+		break;
+	default:
 #ifdef WM_DEBUG
-			device_printf(dev, "%s: PHYTYPE = 0x%x, addr = %02x\n",
+		if ((reg >> MII_ADDRBITS) != 0)
+			device_printf(dev,
+			    "%s: PHYTYPE = 0x%x, addr = 0x%02x\n",
 			    __func__, sc->sc_phytype, reg);
 #endif
-			break;
-		}
+		break;
 	}
 
 	return wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK, val);
@@ -12125,23 +12237,25 @@ wm_gmii_i82544_writereg_locked(device_t 
 	struct wm_softc *sc = device_private(dev);
 	int rv;
 
-	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) {
 			rv = wm_gmii_mdic_writereg(dev, phy,
 			    IGPHY_PAGE_SELECT, reg);
 			if (rv != 0)
 				return rv;
-			break;
-		default:
+		}
+		break;
+	default:
 #ifdef WM_DEBUG
-			device_printf(dev, "%s: PHYTYPE == 0x%x, addr = %02x",
+		if ((reg >> MII_ADDRBITS) != 0)
+			device_printf(dev,
+			    "%s: PHYTYPE == 0x%x, addr = 0x%02x",
 			    __func__, sc->sc_phytype, reg);
 #endif
-			break;
-		}
+		break;
 	}
 
 	return 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.48.4.1 src/sys/dev/pci/if_wmvar.h:1.48.4.2
--- src/sys/dev/pci/if_wmvar.h:1.48.4.1	Thu Jun 22 08:14:35 2023
+++ src/sys/dev/pci/if_wmvar.h	Mon Sep  4 17:45:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wmvar.h,v 1.48.4.1 2023/06/22 08:14:35 martin Exp $	*/
+/*	$NetBSD: if_wmvar.h,v 1.48.4.2 2023/09/04 17:45:24 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