Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 14 Nov 2010 13:26:10 +0000 (UTC)
From:      Marius Strobl <marius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r215297 - in head: . sys/dev/bce sys/dev/bge sys/dev/mii sys/dev/msk sys/dev/nfe sys/dev/stge sys/net
Message-ID:  <201011141326.oAEDQARr083126@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marius
Date: Sun Nov 14 13:26:10 2010
New Revision: 215297
URL: http://svn.freebsd.org/changeset/base/215297

Log:
  o Flesh out the generic IEEE 802.3 annex 31B full duplex flow control
    support in mii(4):
    - Merge generic flow control advertisement (which can be enabled by
      passing by MIIF_DOPAUSE to mii_attach(9)) and parsing support from
      NetBSD into mii_physubr.c and ukphy_subr.c. Unlike as in NetBSD,
      IFM_FLOW isn't implemented as a global option via the "don't care
      mask" but instead as a media specific option this. This has the
      following advantages:
      o allows flow control advertisement with autonegotiation to be
        turned on and off via ifconfig(8) with the default typically
        being off (though MIIF_FORCEPAUSE has been added causing flow
        control to be always advertised, allowing to easily MFC this
        changes for drivers that previously used home-grown support for
        flow control that behaved that way without breaking POLA)
      o allows to deal with PHY drivers where flow control advertisement
        with manual selection doesn't work or at least isn't implemented,
        like it's the case with brgphy(4), e1000phy(4) and ip1000phy(4),
        by setting MIIF_NOMANPAUSE
      o the available combinations of media options are readily available
        from the `ifconfig -m` output
    - Add IFM_FLOW to IFM_SHARED_OPTION_DESCRIPTIONS and IFM_ETH_RXPAUSE
      and IFM_ETH_TXPAUSE to IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS so
      these are understood by ifconfig(8).
  o Make the master/slave support in mii(4) actually usable:
    - Change IFM_ETH_MASTER from being implemented as a global option via
      the "don't care mask" to a media specific one as it actually is only
      applicable to IFM_1000_T to date.
    - Let mii_phy_setmedia() set GTCR_MAN_MS in IFM_1000_T slave mode to
      actually configure manually selected slave mode (like we also do in
      the PHY specific implementations).
    - Add IFM_ETH_MASTER to IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS so it
      is understood by ifconfig(8).
  o Switch bge(4), bce(4), msk(4), nfe(4) and stge(4) along with brgphy(4),
    e1000phy(4) and ip1000phy(4) to use the generic flow control support
    instead of home-grown solutions via IFM_FLAGs. This includes changing
    these PHY drivers and smcphy(4) to no longer unconditionally advertise
    support for flow control but only if the selected media has IFM_FLOW
    set (or MIIF_FORCEPAUSE is set) and implemented for these media variants,
    i.e. typically only for copper.
  o Switch brgphy(4), ciphy(4), e1000phy(4) and ip1000phy(4) to report and
    set IFM_1000_T master mode via IFM_ETH_MASTER instead of via IFF_LINK0
    and some IFM_FLAGn.
  o Switch brgphy(4) to add at least the the supported copper media based on
    the contents of the BMSR via mii_phy_add_media() instead of hardcoding
    them. The latter approach seems to have developed historically, besides
    causing unnecessary code duplication it was also undesirable because
    brgphy_mii_phy_auto() already based the capability advertisement on the
    contents of the BMSR though.
  o Let brgphy(4) set IFM_1000_T master mode on all supported PHY and not
    just BCM5701. Apparently this was a misinterpretation of a workaround
    in the Linux tg3 driver; BCM5701 seem to require RGPHY_1000CTL_MSE and
    BRGPHY_1000CTL_MSC to be set when configuring autonegotiation but
    this doesn't mean we can't set these as well on other PHYs for manual
    media selection.
  o Let ukphy_status() report IFM_1000_T master mode via IFM_ETH_MASTER so
    IFM_1000_T master mode support now is generally available with all PHY
    drivers.
  o Don't let e1000phy(4) set master/slave bits for IFM_1000_SX as it's
    not applicable there.
  
  Reviewed by:	yongari (plus additional testing)
  Obtained from:	NetBSD (partially), OpenBSD (partially)
  MFC after:	2 weeks

Modified:
  head/UPDATING
  head/sys/dev/bce/if_bce.c
  head/sys/dev/bge/if_bge.c
  head/sys/dev/mii/brgphy.c
  head/sys/dev/mii/ciphy.c
  head/sys/dev/mii/e1000phy.c
  head/sys/dev/mii/ip1000phy.c
  head/sys/dev/mii/mii.h
  head/sys/dev/mii/mii_physubr.c
  head/sys/dev/mii/miivar.h
  head/sys/dev/mii/smcphy.c
  head/sys/dev/mii/ukphy_subr.c
  head/sys/dev/msk/if_msk.c
  head/sys/dev/nfe/if_nfe.c
  head/sys/dev/stge/if_stge.c
  head/sys/net/if_media.h

Modified: head/UPDATING
==============================================================================
--- head/UPDATING	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/UPDATING	Sun Nov 14 13:26:10 2010	(r215297)
@@ -22,6 +22,32 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 9.
 	machines to maximize performance.  (To disable malloc debugging, run
 	ln -s aj /etc/malloc.conf.)
 
+20101114:
+	Generic IEEE 802.3 annex 31B full duplex flow control support has been
+	added to mii(4) and bge(4), bce(4), msk(4), nfe(4) and stge(4) along
+	with brgphy(4), e1000phy(4) as well as ip1000phy() have been converted
+	to take advantage of it instead of using custom implementations.  This
+	means that these drivers now no longer unconditionally advertise
+	support for flow control but only do so if flow control is a selected
+	media option.  This was implemented in the generic support that way in
+	order to allow flow control to be switched on and off via ifconfig(8)
+	with the PHY specific default to typically off in order to protect
+	from unwanted effects.  Consequently, if you used flow control with
+	one of the above mentioned drivers you now need to explicitly enable
+	it, for example via:
+		ifconfig bge0 media auto mediaopt flowcontrol
+
+	Along with the above mentioned changes generic support for setting
+	1000baseT master mode also has been added and brgphy(4), ciphy(4),
+	e1000phy(4) as well as ip1000phy(4) have been converted to take
+	advantage of it.  This means that these drivers now no longer take the
+	link0 parameter for selecting master mode but the master media option
+	has to be used instead, for example like in the following:
+		ifconfig bge0 media 1000baseT mediaopt full-duplex,master
+
+	Selection of master mode now is also available with all other PHY
+	drivers supporting 1000baseT.
+
 20101111:
 	The TCP stack has received a significant update to add support for
 	modularised congestion control and generally improve the clarity of

Modified: head/sys/dev/bce/if_bce.c
==============================================================================
--- head/sys/dev/bce/if_bce.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/bce/if_bce.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -1143,7 +1143,7 @@ bce_attach(device_t dev)
 	/* MII child bus by attaching the PHY. */
 	rc = mii_attach(dev, &sc->bce_miibus, ifp, bce_ifmedia_upd,
 	    bce_ifmedia_sts, BMSR_DEFCAPMASK, sc->bce_phy_addr,
-	    MII_OFFSET_ANY, 0);
+	    MII_OFFSET_ANY, MIIF_DOPAUSE);
 	if (rc != 0) {
 		BCE_PRINTF("%s(%d): attaching PHYs failed\n", __FILE__,
 		    __LINE__);
@@ -1769,8 +1769,7 @@ bce_miibus_statchg(device_t dev)
 
 	REG_WR(sc, BCE_EMAC_MODE, val);
 
-	/* FLAG0 is set if RX is enabled and FLAG1 if TX is enabled */
- 	if (mii->mii_media_active & IFM_FLAG0) {
+ 	if ((mii->mii_media_active & IFM_ETH_RXPAUSE) != 0) {
 		DBPRINT(sc, BCE_INFO_PHY,
 		    "%s(): Enabling RX flow control.\n", __FUNCTION__);
 		BCE_SETBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
@@ -1780,7 +1779,7 @@ bce_miibus_statchg(device_t dev)
 		BCE_CLRBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
 	}
 
- 	if (mii->mii_media_active & IFM_FLAG1) {
+ 	if ((mii->mii_media_active & IFM_ETH_TXPAUSE) != 0) {
 		DBPRINT(sc, BCE_INFO_PHY,
 		    "%s(): Enabling TX flow control.\n", __FUNCTION__);
 		BCE_SETBIT(sc, BCE_EMAC_TX_MODE, BCE_EMAC_TX_MODE_FLOW_EN);

Modified: head/sys/dev/bge/if_bge.c
==============================================================================
--- head/sys/dev/bge/if_bge.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/bge/if_bge.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -914,11 +914,13 @@ bge_miibus_statchg(device_t dev)
 
 	if (IFM_OPTIONS(mii->mii_media_active & IFM_FDX) != 0) {
 		BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX);
-		if (IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG1)
+		if ((IFM_OPTIONS(mii->mii_media_active) &
+		    IFM_ETH_TXPAUSE) != 0)
 			BGE_SETBIT(sc, BGE_TX_MODE, BGE_TXMODE_FLOWCTL_ENABLE);
 		else
 			BGE_CLRBIT(sc, BGE_TX_MODE, BGE_TXMODE_FLOWCTL_ENABLE);
-		if (IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0)
+		if ((IFM_OPTIONS(mii->mii_media_active) &
+		    IFM_ETH_RXPAUSE) != 0)
 			BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_FLOWCTL_ENABLE);
 		else
 			BGE_CLRBIT(sc, BGE_RX_MODE, BGE_RXMODE_FLOWCTL_ENABLE);
@@ -3102,9 +3104,9 @@ bge_attach(device_t dev)
 again:
 		bge_asf_driver_up(sc);
 
-		error = (mii_attach(dev, &sc->bge_miibus, ifp,
+		error = mii_attach(dev, &sc->bge_miibus, ifp,
 		    bge_ifmedia_upd, bge_ifmedia_sts, BMSR_DEFCAPMASK,
-		    phy_addr, MII_OFFSET_ANY, 0));
+		    phy_addr, MII_OFFSET_ANY, MIIF_DOPAUSE);
 		if (error != 0) {
 			if (trys++ < 4) {
 				device_printf(sc->bge_dev, "Try again\n");

Modified: head/sys/dev/mii/brgphy.c
==============================================================================
--- head/sys/dev/mii/brgphy.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/brgphy.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -99,9 +99,9 @@ static driver_t brgphy_driver = {
 DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
 
 static int	brgphy_service(struct mii_softc *, struct mii_data *, int);
-static void	brgphy_setmedia(struct mii_softc *, int, int);
+static void	brgphy_setmedia(struct mii_softc *, int);
 static void	brgphy_status(struct mii_softc *);
-static void	brgphy_mii_phy_auto(struct mii_softc *);
+static void	brgphy_mii_phy_auto(struct mii_softc *, int);
 static void	brgphy_reset(struct mii_softc *);
 static void	brgphy_enable_loopback(struct mii_softc *);
 static void	bcm5401_load_dspcode(struct mii_softc *);
@@ -169,6 +169,7 @@ detect_hs21(struct bce_softc *bce_sc)
 static int
 brgphy_probe(device_t dev)
 {
+
 	return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT));
 }
 
@@ -183,7 +184,6 @@ brgphy_attach(device_t dev)
 	struct mii_attach_args *ma;
 	struct mii_data *mii;
 	struct ifnet *ifp;
-	int fast_ether;
 
 	bsc = device_get_softc(dev);
 	sc = &bsc->mii_sc;
@@ -203,8 +203,7 @@ brgphy_attach(device_t dev)
 	 * At least some variants wedge when isolating, at least some also
 	 * don't support loopback.
 	 */
-	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
-	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
+	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP | MIIF_NOMANPAUSE;
 
 	/* Initialize brgphy_softc structure */
 	bsc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
@@ -212,8 +211,6 @@ brgphy_attach(device_t dev)
 	bsc->mii_rev = MII_REV(ma->mii_id2);
 	bsc->serdes_flags = 0;
 
-	fast_ether = 0;
-
 	if (bootverbose)
 		device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
 		    bsc->mii_oui, bsc->mii_model, bsc->mii_rev);
@@ -279,8 +276,7 @@ brgphy_attach(device_t dev)
 	    pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5901A2 ||
 	    pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5906 ||
 	    pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5906M)) {
-		fast_ether = 1;
-		sc->mii_anegticks = MII_ANEGTICKS;
+		ma->mii_capmask &= ~BMSR_EXTSTAT;
 	}
 
 	brgphy_reset(sc);
@@ -295,27 +291,10 @@ brgphy_attach(device_t dev)
 
 	/* Add the supported media types */
 	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
-			BRGPHY_S10);
-		printf("10baseT, ");
-		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
-			BRGPHY_S10 | BRGPHY_BMCR_FDX);
-		printf("10baseT-FDX, ");
-		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
-			BRGPHY_S100);
-		printf("100baseTX, ");
-		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
-			BRGPHY_S100 | BRGPHY_BMCR_FDX);
-		printf("100baseTX-FDX, ");
-		if (fast_ether == 0) {
-			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
-				BRGPHY_S1000);
-			printf("1000baseT, ");
-			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
-				BRGPHY_S1000 | BRGPHY_BMCR_FDX);
-			printf("1000baseT-FDX, ");
-		}
+		mii_phy_add_media(sc);
+		printf("\n");
 	} else {
+		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst),
 			BRGPHY_S1000 | BRGPHY_BMCR_FDX);
 		printf("1000baseSX-FDX, ");
@@ -337,11 +316,10 @@ brgphy_attach(device_t dev)
 			printf("auto-neg workaround, ");
 			bsc->serdes_flags |= BRGPHY_NOANWAIT;
 		}
+		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
+		printf("auto\n");
 	}
 
-	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
-	printf("auto\n");
-
 #undef ADD
 	MIIBUS_MEDIAINIT(sc->mii_dev);
 	return (0);
@@ -367,15 +345,14 @@ brgphy_service(struct mii_softc *sc, str
 
 		switch (IFM_SUBTYPE(ife->ifm_media)) {
 		case IFM_AUTO:
-			brgphy_mii_phy_auto(sc);
+			brgphy_mii_phy_auto(sc, ife->ifm_media);
 			break;
 		case IFM_2500_SX:
 		case IFM_1000_SX:
 		case IFM_1000_T:
 		case IFM_100_TX:
 		case IFM_10_T:
-			brgphy_setmedia(sc, ife->ifm_media,
-			    mii->mii_ifp->if_flags & IFF_LINK0);
+			brgphy_setmedia(sc, ife->ifm_media);
 			break;
 		default:
 			return (EINVAL);
@@ -397,7 +374,7 @@ brgphy_service(struct mii_softc *sc, str
 		 * Check to see if we have link.  If we do, we don't
 		 * need to restart the autonegotiation process.
 		 */
-		val	= PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+		val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
 		if (val & BMSR_LINK) {
 			sc->mii_ticks = 0;	/* Reset autoneg timer. */
 			break;
@@ -414,7 +391,7 @@ brgphy_service(struct mii_softc *sc, str
 
 		/* Retry autonegotiation */
 		sc->mii_ticks = 0;
-		brgphy_mii_phy_auto(sc);
+		brgphy_mii_phy_auto(sc, ife->ifm_media);
 		break;
 	}
 
@@ -456,7 +433,6 @@ brgphy_service(struct mii_softc *sc, str
 	return (0);
 }
 
-
 /****************************************************************************/
 /* Sets the PHY link speed.                                                 */
 /*                                                                          */
@@ -464,12 +440,10 @@ brgphy_service(struct mii_softc *sc, str
 /*   None                                                                   */
 /****************************************************************************/
 static void
-brgphy_setmedia(struct mii_softc *sc, int media, int master)
+brgphy_setmedia(struct mii_softc *sc, int media)
 {
-	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
 	int bmcr = 0, gig;
 
-	/* Calculate the value for the BMCR register. */
 	switch (IFM_SUBTYPE(media)) {
 	case IFM_2500_SX:
 		break;
@@ -486,7 +460,6 @@ brgphy_setmedia(struct mii_softc *sc, in
 		break;
 	}
 
-	/* Calculate duplex settings for 1000BasetT/1000BaseX. */
 	if ((media & IFM_GMASK) == IFM_FDX) {
 		bmcr |= BRGPHY_BMCR_FDX;
 		gig = BRGPHY_1000CTL_AFD;
@@ -494,53 +467,30 @@ brgphy_setmedia(struct mii_softc *sc, in
 		gig = BRGPHY_1000CTL_AHD;
 	}
 
-	/* Force loopback to disconnect PHY for Ethernet medium. */
+	/* Force loopback to disconnect PHY from Ethernet medium. */
 	brgphy_enable_loopback(sc);
 
-	/* Disable 1000BaseT advertisements. */
 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
-	/* Disable 10/100 advertisements. */
 	PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
-	/* Write forced link speed. */
-	PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr);
 
-	/* If 10/100 only then configuration is complete. */
-	if ((IFM_SUBTYPE(media) != IFM_1000_T) && (IFM_SUBTYPE(media) != IFM_1000_SX))
-		goto brgphy_setmedia_exit;
+	if (IFM_SUBTYPE(media) != IFM_1000_T &&
+	    IFM_SUBTYPE(media) != IFM_1000_SX) {
+		PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr);
+		return;
+	}
 
-	/* Set duplex speed advertisement for 1000BaseT/1000BaseX. */
+	if (IFM_SUBTYPE(media) == IFM_1000_T) {
+		gig |= BRGPHY_1000CTL_MSE;
+		if ((media & IFM_ETH_MASTER) != 0)
+			gig |= BRGPHY_1000CTL_MSC;
+	}
 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
-	/* Restart auto-negotiation for 1000BaseT/1000BaseX. */
 	PHY_WRITE(sc, BRGPHY_MII_BMCR,
 	    bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
-
-	/* If not 5701 PHY then configuration is complete. */
-	if (bsc->mii_model != MII_MODEL_xxBROADCOM_BCM5701)
-		goto brgphy_setmedia_exit;
-
-	/*
-	 * When setting the link manually, one side must be the master and
-	 * the other the slave. However ifmedia doesn't give us a good way
-	 * to specify this, so we fake it by using one of the LINK flags.
-	 * If LINK0 is set, we program the PHY to be a master, otherwise
-	 * it's a slave.
-	 */
-	if (master) {
-		PHY_WRITE(sc, BRGPHY_MII_1000CTL,
-		    gig | BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC);
-	} else {
-		PHY_WRITE(sc, BRGPHY_MII_1000CTL,
-		    gig | BRGPHY_1000CTL_MSE);
-	}
-
-brgphy_setmedia_exit:
-	return;
 }
 
 /****************************************************************************/
 /* Set the media status based on the PHY settings.                          */
-/* IFM_FLAG0 = 0 (RX flow control disabled) | 1 (enabled)                   */
-/* IFM_FLAG1 = 0 (TX flow control disabled) | 1 (enabled)                   */
 /*                                                                          */
 /* Returns:                                                                 */
 /*   None                                                                   */
@@ -550,34 +500,34 @@ brgphy_status(struct mii_softc *sc)
 {
 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
 	struct mii_data *mii = sc->mii_pdata;
-	int aux, bmcr, bmsr, anar, anlpar, xstat, val;
-
+	int aux, bmcr, bmsr, val, xstat;
+	u_int flowstat;
 
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
 	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
 	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
-	anar = PHY_READ(sc, BRGPHY_MII_ANAR);
-	anlpar = PHY_READ(sc, BRGPHY_MII_ANLPAR);
 
-	/* Loopback is enabled. */
 	if (bmcr & BRGPHY_BMCR_LOOP) {
-
 		mii->mii_media_active |= IFM_LOOP;
 	}
 
-	/* Autoneg is still in progress. */
 	if ((bmcr & BRGPHY_BMCR_AUTOEN) &&
 	    (bmsr & BRGPHY_BMSR_ACOMP) == 0 &&
 	    (bsc->serdes_flags & BRGPHY_NOANWAIT) == 0) {
 		/* Erg, still trying, I guess... */
 		mii->mii_media_active |= IFM_NONE;
-		goto brgphy_status_exit;
+		return;
 	}
 
-	/* Autoneg is enabled and complete, link should be up. */
 	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
+		/*
+		 * NB: reading the ANAR, ANLPAR or 1000STS after the AUXSTS
+		 * wedges at least the PHY of BCM5704 (but not others).
+		 */
+		flowstat = mii_phy_flowstatus(sc);
+		xstat = PHY_READ(sc, BRGPHY_MII_1000STS);
 		aux = PHY_READ(sc, BRGPHY_MII_AUXSTS);
 
 		/* If copper link is up, get the negotiated speed/duplex. */
@@ -601,8 +551,16 @@ brgphy_status(struct mii_softc *sc)
 			default:
 				mii->mii_media_active |= IFM_NONE; break;
 			}
+
+			if ((mii->mii_media_active & IFM_FDX) != 0)
+				mii->mii_media_active |= flowstat;
+
+			if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T &&
+			    (xstat & BRGPHY_1000STS_MSR) != 0)
+				mii->mii_media_active |= IFM_ETH_MASTER;
 		}
 	} else {
+		/* Todo: Add support for flow control. */
 		/* If serdes link is up, get the negotiated speed/duplex. */
 		if (bmsr & BRGPHY_BMSR_LINK) {
 			mii->mii_media_status |= IFM_ACTIVE;
@@ -620,7 +578,6 @@ brgphy_status(struct mii_softc *sc)
 				else
 					mii->mii_media_active |= IFM_HDX;
 			}
-
 		} else if (bsc->serdes_flags & BRGPHY_5708S) {
 			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
 			xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1);
@@ -642,9 +599,7 @@ brgphy_status(struct mii_softc *sc)
 				mii->mii_media_active |= IFM_FDX;
 			else
 				mii->mii_media_active |= IFM_HDX;
-
 		} else if (bsc->serdes_flags & BRGPHY_5709S) {
-
 			/* Select GP Status Block of the AN MMD, get autoneg results. */
 			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_GP_STATUS);
 			xstat = PHY_READ(sc, BRGPHY_GP_STATUS_TOP_ANEG_STATUS);
@@ -670,65 +625,42 @@ brgphy_status(struct mii_softc *sc)
 			else
 				mii->mii_media_active |= IFM_HDX;
 		}
-
 	}
-
-	/* Todo: Change bge to use these settings. */
-
-	/* Fetch flow control settings from the copper PHY. */
-	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-		/* Set FLAG0 if RX is enabled and FLAG1 if TX is enabled */
-		if ((anar & BRGPHY_ANAR_PC) && (anlpar & BRGPHY_ANLPAR_PC)) {
-			mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
-		} else if (!(anar & BRGPHY_ANAR_PC) && (anlpar & BRGPHY_ANAR_ASP) &&
-		    (anlpar & BRGPHY_ANLPAR_PC) && (anlpar & BRGPHY_ANLPAR_ASP)) {
-			mii->mii_media_active |= IFM_FLAG1;
-		} else if ((anar & BRGPHY_ANAR_PC) && (anar & BRGPHY_ANAR_ASP) &&
-		    !(anlpar & BRGPHY_ANLPAR_PC) && (anlpar & BRGPHY_ANLPAR_ASP)) {
-			mii->mii_media_active |= IFM_FLAG0;
-		}
-	}
-
-	/* Todo: Add support for fiber settings too. */
-
-
-brgphy_status_exit:
-	return;
 }
 
 static void
-brgphy_mii_phy_auto(struct mii_softc *sc)
+brgphy_mii_phy_auto(struct mii_softc *sc, int media)
 {
 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
-	int ktcr = 0;
+	int anar, ktcr = 0;
 
 	brgphy_reset(sc);
 
-	/* Enable flow control in the advertisement register. */
 	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-		/* Pause capability advertisement (pause capable & asymmetric) */
-		PHY_WRITE(sc, BRGPHY_MII_ANAR,
-	    	BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA |
-	    	BRGPHY_ANAR_ASP | BRGPHY_ANAR_PC);
+		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
+		if ((media & IFM_FLOW) != 0 ||
+		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+			anar |= BRGPHY_ANAR_PC | BRGPHY_ANAR_ASP;
+		PHY_WRITE(sc, BRGPHY_MII_ANAR, anar);
 	} else {
-		PHY_WRITE(sc, BRGPHY_SERDES_ANAR, BRGPHY_SERDES_ANAR_FDX |
-			BRGPHY_SERDES_ANAR_HDX | BRGPHY_SERDES_ANAR_BOTH_PAUSE);
+		anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX;
+		if ((media & IFM_FLOW) != 0 ||
+		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+			anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE;
+		PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar);
 	}
 
-	/* Enable speed in the 1000baseT control register */
 	ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD;
 	if (bsc->mii_model == MII_MODEL_xxBROADCOM_BCM5701)
 		ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC;
 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
 	ktcr = PHY_READ(sc, BRGPHY_MII_1000CTL);
 
-	/* Start autonegotiation */
-	PHY_WRITE(sc, BRGPHY_MII_BMCR,BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
+	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_AUTOEN |
+	    BRGPHY_BMCR_STARTNEG);
 	PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
-
 }
 
-
 /* Enable loopback to force the link down. */
 static void
 brgphy_enable_loopback(struct mii_softc *sc)
@@ -923,7 +855,6 @@ brgphy_fixup_jitter_bug(struct mii_softc
 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
 }
 
-
 static void
 brgphy_fixup_disable_early_dac(struct mii_softc *sc)
 {
@@ -936,7 +867,6 @@ brgphy_fixup_disable_early_dac(struct mi
 
 }
 
-
 static void
 brgphy_ethernet_wirespeed(struct mii_softc *sc)
 {
@@ -948,7 +878,6 @@ brgphy_ethernet_wirespeed(struct mii_sof
 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4));
 }
 
-
 static void
 brgphy_jumbo_settings(struct mii_softc *sc, u_long mtu)
 {
@@ -989,7 +918,7 @@ brgphy_reset(struct mii_softc *sc)
 	struct bge_softc *bge_sc = NULL;
 	struct bce_softc *bce_sc = NULL;
 	struct ifnet *ifp;
-    int val;
+	int val;
 
 	/* Perform a standard PHY reset. */
 	mii_phy_reset(sc);
@@ -1029,7 +958,6 @@ brgphy_reset(struct mii_softc *sc)
 		bce_sc = ifp->if_softc;
 	}
 
-	/* Handle any bge (NetXtreme/NetLink) workarounds. */
 	if (bge_sc) {
 		/* Fix up various bugs */
 		if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG)
@@ -1060,10 +988,7 @@ brgphy_reset(struct mii_softc *sc)
 		/* Adjust output voltage (From Linux driver) */
 		if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906)
 			PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12);
-
-	/* Handle any bce (NetXtreme II) workarounds. */
 	} else if (bce_sc) {
-
 		if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 &&
 			(bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) {
 
@@ -1154,7 +1079,6 @@ brgphy_reset(struct mii_softc *sc)
 
 			/* Restore IEEE0 block (assumed in all brgphy(4) code). */
 			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0);
-
         } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709) {
 			if ((BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Ax) ||
 				(BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Bx))
@@ -1167,6 +1091,5 @@ brgphy_reset(struct mii_softc *sc)
 			brgphy_jumbo_settings(sc, ifp->if_mtu);
 			brgphy_ethernet_wirespeed(sc);
 		}
-
 	}
 }

Modified: head/sys/dev/mii/ciphy.c
==============================================================================
--- head/sys/dev/mii/ciphy.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/ciphy.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -165,7 +165,7 @@ ciphy_service(struct mii_softc *sc, stru
 			if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN)
 				return (0);
 #endif
-			(void) mii_phy_auto(sc);
+			(void)mii_phy_auto(sc);
 			break;
 		case IFM_1000_T:
 			speed = CIPHY_S1000;
@@ -190,30 +190,16 @@ setit:
 			if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
 				break;
 
+			gig |= CIPHY_1000CTL_MSE;
+			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
+				gig |= CIPHY_1000CTL_MSC;
 			PHY_WRITE(sc, CIPHY_MII_1000CTL, gig);
 			PHY_WRITE(sc, CIPHY_MII_BMCR,
-			    speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG);
-
-			/*
-			 * When setting the link manually, one side must
-			 * be the master and the other the slave. However
-			 * ifmedia doesn't give us a good way to specify
-			 * this, so we fake it by using one of the LINK
-			 * flags. If LINK0 is set, we program the PHY to
-			 * be a master, otherwise it's a slave.
-			 */
-			if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
-				PHY_WRITE(sc, CIPHY_MII_1000CTL,
-				    gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC);
-			} else {
-				PHY_WRITE(sc, CIPHY_MII_1000CTL,
-				    gig|CIPHY_1000CTL_MSE);
-			}
+			    speed | CIPHY_BMCR_AUTOEN | CIPHY_BMCR_STARTNEG);
 			break;
 		case IFM_NONE:
-			PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
+			PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN);
 			break;
-		case IFM_100_T4:
 		default:
 			return (EINVAL);
 		}
@@ -319,6 +305,10 @@ ciphy_status(struct mii_softc *sc)
 		mii->mii_media_active |= IFM_FDX;
 	else
 		mii->mii_media_active |= IFM_HDX;
+
+	if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
+	   (PHY_READ(sc, CIPHY_MII_1000STS) & CIPHY_1000STS_MSR) != 0)
+		mii->mii_media_active |= IFM_ETH_MASTER;
 }
 
 static void

Modified: head/sys/dev/mii/e1000phy.c
==============================================================================
--- head/sys/dev/mii/e1000phy.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/e1000phy.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -91,7 +91,7 @@ DRIVER_MODULE(e1000phy, miibus, e1000phy
 static int	e1000phy_service(struct mii_softc *, struct mii_data *, int);
 static void	e1000phy_status(struct mii_softc *);
 static void	e1000phy_reset(struct mii_softc *);
-static int	e1000phy_mii_phy_auto(struct e1000phy_softc *);
+static int	e1000phy_mii_phy_auto(struct e1000phy_softc *, int);
 
 static const struct mii_phydesc e1000phys[] = {
 	MII_PHY_DESC(MARVELL, E1000),
@@ -146,6 +146,8 @@ e1000phy_attach(device_t dev)
 	sc->mii_service = e1000phy_service;
 	sc->mii_pdata = mii;
 
+	sc->mii_flags |= MIIF_NOMANPAUSE;
+
 	esc->mii_model = MII_MODEL(ma->mii_id2);
 	ifp = sc->mii_pdata->mii_ifp;
 	if (strcmp(ifp->if_dname, "msk") == 0 &&
@@ -323,7 +325,7 @@ e1000phy_service(struct mii_softc *sc, s
 			break;
 
 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
-			e1000phy_mii_phy_auto(esc);
+			e1000phy_mii_phy_auto(esc, ife->ifm_media);
 			break;
 		}
 
@@ -366,27 +368,14 @@ e1000phy_service(struct mii_softc *sc, s
 		reg &= ~E1000_CR_AUTO_NEG_ENABLE;
 		PHY_WRITE(sc, E1000_CR, reg | E1000_CR_RESET);
 
-		/*
-		 * When setting the link manually, one side must
-		 * be the master and the other the slave. However
-		 * ifmedia doesn't give us a good way to specify
-		 * this, so we fake it by using one of the LINK
-		 * flags. If LINK0 is set, we program the PHY to
-		 * be a master, otherwise it's a slave.
-		 */
-		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T ||
-		    (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_SX)) {
-			if ((mii->mii_ifp->if_flags & IFF_LINK0))
-				PHY_WRITE(sc, E1000_1GCR, gig |
-				    E1000_1GCR_MS_ENABLE | E1000_1GCR_MS_VALUE);
-			else
-				PHY_WRITE(sc, E1000_1GCR, gig |
-				    E1000_1GCR_MS_ENABLE);
-		} else {
-			if ((sc->mii_extcapabilities &
-			    (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
-				PHY_WRITE(sc, E1000_1GCR, 0);
-		}
+		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
+			gig |= E1000_1GCR_MS_ENABLE;
+			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)	
+				gig |= E1000_1GCR_MS_VALUE;
+			PHY_WRITE(sc, E1000_1GCR, gig);
+		} else if ((sc->mii_extcapabilities &
+		    (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
+			PHY_WRITE(sc, E1000_1GCR, 0);
 		PHY_WRITE(sc, E1000_AR, E1000_AR_SELECTOR_FIELD);
 		PHY_WRITE(sc, E1000_CR, speed | E1000_CR_RESET);
 done:
@@ -424,7 +413,7 @@ done:
 
 		sc->mii_ticks = 0;
 		e1000phy_reset(sc);
-		e1000phy_mii_phy_auto(esc);
+		e1000phy_mii_phy_auto(esc, ife->ifm_media);
 		break;
 	}
 
@@ -440,7 +429,7 @@ static void
 e1000phy_status(struct mii_softc *sc)
 {
 	struct mii_data *mii = sc->mii_pdata;
-	int bmcr, bmsr, gsr, ssr, ar, lpar;
+	int bmcr, bmsr, ssr;
 
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
@@ -485,38 +474,22 @@ e1000phy_status(struct mii_softc *sc)
 		mii->mii_media_active |= IFM_1000_SX;
 	}
 
-	if (ssr & E1000_SSR_DUPLEX)
+	if (ssr & E1000_SSR_DUPLEX) {
 		mii->mii_media_active |= IFM_FDX;
-	else
+		if ((sc->mii_flags & MIIF_HAVEFIBER) == 0)
+			mii->mii_media_active |= mii_phy_flowstatus(sc);
+	} else
 		mii->mii_media_active |= IFM_HDX;
 
-	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-		ar = PHY_READ(sc, E1000_AR);
-		lpar = PHY_READ(sc, E1000_LPAR);
-		/* FLAG0==rx-flow-control FLAG1==tx-flow-control */
-		if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) {
-			mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
-		} else if (!(ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
-		    (lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
-			mii->mii_media_active |= IFM_FLAG1;
-		} else if ((ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
-		    !(lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
-			mii->mii_media_active |= IFM_FLAG0;
-		}
-	}
-
-	/* FLAG2 : local PHY resolved to MASTER */
-	if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) ||
-	    (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)) {
-		PHY_READ(sc, E1000_1GSR);
-		gsr = PHY_READ(sc, E1000_1GSR);
-		if ((gsr & E1000_1GSR_MS_CONFIG_RES) != 0)
-			mii->mii_media_active |= IFM_FLAG2;
+	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
+		if (((PHY_READ(sc, E1000_1GSR) | PHY_READ(sc, E1000_1GSR)) &
+		    E1000_1GSR_MS_CONFIG_RES) != 0)
+			mii->mii_media_active |= IFM_ETH_MASTER;
 	}
 }
 
 static int
-e1000phy_mii_phy_auto(struct e1000phy_softc *esc)
+e1000phy_mii_phy_auto(struct e1000phy_softc *esc, int media)
 {
 	struct mii_softc *sc;
 	uint16_t reg;
@@ -525,12 +498,13 @@ e1000phy_mii_phy_auto(struct e1000phy_so
 	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
 		reg = PHY_READ(sc, E1000_AR);
 		reg |= E1000_AR_10T | E1000_AR_10T_FD |
-		    E1000_AR_100TX | E1000_AR_100TX_FD |
-		    E1000_AR_PAUSE | E1000_AR_ASM_DIR;
+		    E1000_AR_100TX | E1000_AR_100TX_FD;
+		if ((media & IFM_FLOW) != 0 ||
+		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+			reg |= E1000_AR_PAUSE | E1000_AR_ASM_DIR;
 		PHY_WRITE(sc, E1000_AR, reg | E1000_AR_SELECTOR_FIELD);
 	} else
-		PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X |
-		    E1000_FA_SYM_PAUSE | E1000_FA_ASYM_PAUSE);
+		PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X);
 	if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
 		PHY_WRITE(sc, E1000_1GCR,
 		    E1000_1GCR_1000T_FD | E1000_1GCR_1000T);

Modified: head/sys/dev/mii/ip1000phy.c
==============================================================================
--- head/sys/dev/mii/ip1000phy.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/ip1000phy.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -84,7 +84,7 @@ DRIVER_MODULE(ip1000phy, miibus, ip1000p
 static int	ip1000phy_service(struct mii_softc *, struct mii_data *, int);
 static void	ip1000phy_status(struct mii_softc *);
 static void	ip1000phy_reset(struct mii_softc *);
-static int	ip1000phy_mii_phy_auto(struct mii_softc *);
+static int	ip1000phy_mii_phy_auto(struct mii_softc *, int);
 
 static const struct mii_phydesc ip1000phys[] = {
 	MII_PHY_DESC(ICPLUS, IP1000A),
@@ -120,7 +120,7 @@ ip1000phy_attach(device_t dev)
 	sc->mii_service = ip1000phy_service;
 	sc->mii_pdata = mii;
 
-	sc->mii_flags |= MIIF_NOISOLATE;
+	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOMANPAUSE;
 
 	isc->model = MII_MODEL(ma->mii_id2);
 	isc->revision = MII_REV(ma->mii_id2);
@@ -163,9 +163,8 @@ ip1000phy_service(struct mii_softc *sc, 
 		ip1000phy_reset(sc);
 		switch (IFM_SUBTYPE(ife->ifm_media)) {
 		case IFM_AUTO:
-			(void)ip1000phy_mii_phy_auto(sc);
+			(void)ip1000phy_mii_phy_auto(sc, ife->ifm_media);
 			goto done;
-			break;
 
 		case IFM_1000_T:
 			/*
@@ -199,26 +198,10 @@ ip1000phy_service(struct mii_softc *sc, 
 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
 			break;
 
+		gig |= IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL;
+		if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
+			gig |= IP1000PHY_1000CR_MMASTER;
 		PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig);
-		PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
-
-		/*
-		 * When setting the link manually, one side must
-		 * be the master and the other the slave. However
-		 * ifmedia doesn't give us a good way to specify
-		 * this, so we fake it by using one of the LINK
-		 * flags. If LINK0 is set, we program the PHY to
-		 * be a master, otherwise it's a slave.
-		 */
-		if ((mii->mii_ifp->if_flags & IFF_LINK0))
-			PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
-			    IP1000PHY_1000CR_MASTER |
-			    IP1000PHY_1000CR_MMASTER |
-			    IP1000PHY_1000CR_MANUAL);
-		else
-			PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
-			    IP1000PHY_1000CR_MASTER |
-			    IP1000PHY_1000CR_MANUAL);
 
 done:
 		break;
@@ -258,7 +241,7 @@ done:
 			break;
 
 		sc->mii_ticks = 0;
-		ip1000phy_mii_phy_auto(sc);
+		ip1000phy_mii_phy_auto(sc, ife->ifm_media);
 		break;
 	}
 
@@ -276,7 +259,6 @@ ip1000phy_status(struct mii_softc *sc)
 	struct ip1000phy_softc *isc;
 	struct mii_data *mii = sc->mii_pdata;
 	uint32_t bmsr, bmcr, stat;
-	uint32_t ar, lpar;
 
 	isc = (struct ip1000phy_softc *)sc;
 
@@ -345,36 +327,18 @@ ip1000phy_status(struct mii_softc *sc)
 			mii->mii_media_active |= IFM_HDX;
 	}
 
-	ar = PHY_READ(sc, IP1000PHY_MII_ANAR);
-	lpar = PHY_READ(sc, IP1000PHY_MII_ANLPAR);
-
-	/*
-	 * FLAG0 : Rx flow-control
-	 * FLAG1 : Tx flow-control
-	 */
-	if ((ar & IP1000PHY_ANAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_PAUSE))
-		mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
-	else if (!(ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
-	    (lpar & IP1000PHY_ANLPAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_APAUSE))
-		mii->mii_media_active |= IFM_FLAG1;
-	else if ((ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
-	    !(lpar & IP1000PHY_ANLPAR_PAUSE) &&
-	    (lpar & IP1000PHY_ANLPAR_APAUSE)) {
-		mii->mii_media_active |= IFM_FLAG0;
-	}
+	if ((mii->mii_media_active & IFM_FDX) != 0)
+		mii->mii_media_active |= mii_phy_flowstatus(sc);
 
-	/*
-	 * FLAG2 : local PHY resolved to MASTER
-	 */
 	if ((mii->mii_media_active & IFM_1000_T) != 0) {
 		stat = PHY_READ(sc, IP1000PHY_MII_1000SR);
 		if ((stat & IP1000PHY_1000SR_MASTER) != 0)
-			mii->mii_media_active |= IFM_FLAG2;
+			mii->mii_media_active |= IFM_ETH_MASTER;
 	}
 }
 
 static int
-ip1000phy_mii_phy_auto(struct mii_softc *sc)
+ip1000phy_mii_phy_auto(struct mii_softc *sc, int media)
 {
 	struct ip1000phy_softc *isc;
 	uint32_t reg;
@@ -386,8 +350,9 @@ ip1000phy_mii_phy_auto(struct mii_softc 
 		reg |= IP1000PHY_ANAR_NP;
 	}
 	reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX |
-	    IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX |
-	    IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE;
+	    IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX;
+	if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+		reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE;
 	PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA);
 
 	reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX;

Modified: head/sys/dev/mii/mii.h
==============================================================================
--- head/sys/dev/mii/mii.h	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/mii.h	Sun Nov 14 13:26:10 2010	(r215297)
@@ -128,6 +128,10 @@
 #define ANAR_10_FD	0x0040	/* local device supports 10bT FD */
 #define ANAR_10		0x0020	/* local device supports 10bT */
 #define	ANAR_CSMA	0x0001	/* protocol selector CSMA/CD */
+#define	ANAR_PAUSE_NONE		(0 << 10)
+#define	ANAR_PAUSE_SYM		(1 << 10)
+#define	ANAR_PAUSE_ASYM		(2 << 10)
+#define	ANAR_PAUSE_TOWARDS	(3 << 10)
 
 #define	ANAR_X_FD	0x0020	/* local device supports 1000BASE-X FD */
 #define	ANAR_X_HD	0x0040	/* local device supports 1000BASE-X HD */
@@ -148,6 +152,11 @@
 #define ANLPAR_10_FD	0x0040	/* link partner supports 10bT FD */
 #define ANLPAR_10	0x0020	/* link partner supports 10bT */
 #define	ANLPAR_CSMA	0x0001	/* protocol selector CSMA/CD */
+#define	ANLPAR_PAUSE_MASK	(3 << 10)
+#define	ANLPAR_PAUSE_NONE	(0 << 10)
+#define	ANLPAR_PAUSE_SYM	(1 << 10)
+#define	ANLPAR_PAUSE_ASYM	(2 << 10)
+#define	ANLPAR_PAUSE_TOWARDS	(3 << 10)
 
 #define	ANLPAR_X_FD	0x0020	/* local device supports 1000BASE-X FD */
 #define	ANLPAR_X_HD	0x0040	/* local device supports 1000BASE-X HD */

Modified: head/sys/dev/mii/mii_physubr.c
==============================================================================
--- head/sys/dev/mii/mii_physubr.c	Sun Nov 14 13:25:01 2010	(r215296)
+++ head/sys/dev/mii/mii_physubr.c	Sun Nov 14 13:26:10 2010	(r215297)
@@ -106,8 +106,13 @@ mii_phy_setmedia(struct mii_softc *sc)
 	int bmcr, anar, gtcr;
 
 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
+		/*
+		 * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG.
+		 * The former is necessary as we might switch from flow-
+		 * control advertisment being off to on or vice versa.
+		 */
 		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 ||
-		    (sc->mii_flags & MIIF_FORCEANEG) != 0)
+		    (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0)
 			(void)mii_phy_auto(sc);
 		return;
 	}
@@ -124,14 +129,23 @@ mii_phy_setmedia(struct mii_softc *sc)
 	bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
 	gtcr = mii_media_table[ife->ifm_data].mm_gtcr;
 
-	if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) {
-		switch (IFM_SUBTYPE(ife->ifm_media)) {
-		case IFM_1000_T:
-			gtcr |= GTCR_MAN_MS | GTCR_ADV_MS;
-			break;
+	if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
+		gtcr |= GTCR_MAN_MS;
+		if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
+			gtcr |= GTCR_ADV_MS;
+	}
 
-		default:
-			printf("mii_phy_setmedia: MASTER on wrong media\n");
+	if ((ife->ifm_media & IFM_GMASK) == (IFM_FDX | IFM_FLOW) ||
+	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0) {
+		if ((sc->mii_flags & MIIF_IS_1000X) != 0)
+			anar |= ANAR_X_PAUSE_TOWARDS;
+		else {
+			anar |= ANAR_FC;
+			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
+			if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 &&
+			    (sc->mii_extcapabilities &
+			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
+				anar |= ANAR_X_PAUSE_ASYM;
 		}
 	}
 
@@ -147,6 +161,7 @@ mii_phy_setmedia(struct mii_softc *sc)
 int
 mii_phy_auto(struct mii_softc *sc)
 {
+	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
 	int anar, gtcr;
 
 	/*
@@ -160,16 +175,23 @@ mii_phy_auto(struct mii_softc *sc)
 		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0)
 			anar |= ANAR_X_HD;
 
-		if ((sc->mii_flags & MIIF_DOPAUSE) != 0) {
-			/* XXX Asymmetric vs. symmetric? */
-			anar |= ANLPAR_X_PAUSE_TOWARDS;
-		}
+		if ((ife->ifm_media & IFM_FLOW) != 0 ||
+		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+			anar |= ANAR_X_PAUSE_TOWARDS;
 		PHY_WRITE(sc, MII_ANAR, anar);
 	} else {
 		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
 		    ANAR_CSMA;
-		if ((sc->mii_flags & MIIF_DOPAUSE) != 0)
-			anar |= ANAR_FC;
+		if ((ife->ifm_media & IFM_FLOW) != 0 ||
+		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0) {
+			if ((sc->mii_capabilities & BMSR_100TXFDX) != 0)
+				anar |= ANAR_FC;
+			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
+			if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) &&
+			    (sc->mii_extcapabilities &
+			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201011141326.oAEDQARr083126>