Page MenuHomeFreeBSD

D55631.id173262.diff
No OneTemporary

D55631.id173262.diff

diff --git a/sys/dev/usb/net/if_axge.c b/sys/dev/usb/net/if_axge.c
--- a/sys/dev/usb/net/if_axge.c
+++ b/sys/dev/usb/net/if_axge.c
@@ -107,6 +107,7 @@
static miibus_readreg_t axge_miibus_readreg;
static miibus_writereg_t axge_miibus_writereg;
static miibus_statchg_t axge_miibus_statchg;
+static miibus_linkchg_t axge_miibus_linkchg;
static uether_fn_t axge_attach_post;
static uether_fn_t axge_init;
@@ -181,6 +182,7 @@
DEVMETHOD(miibus_readreg, axge_miibus_readreg),
DEVMETHOD(miibus_writereg, axge_miibus_writereg),
DEVMETHOD(miibus_statchg, axge_miibus_statchg),
+ DEVMETHOD(miibus_linkchg, axge_miibus_linkchg),
DEVMETHOD_END
};
@@ -329,9 +331,9 @@
struct axge_softc *sc;
struct mii_data *mii;
if_t ifp;
+ int locked, new_link, old_link;
+ uint16_t bmsr, val;
uint8_t link_status, tmp[5];
- uint16_t val;
- int locked;
sc = device_get_softc(dev);
mii = GET_MII(sc);
@@ -344,6 +346,7 @@
(if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
goto done;
+ old_link = (sc->sc_flags & AXGE_FLAG_LINK) ? 1 : 0;
sc->sc_flags &= ~AXGE_FLAG_LINK;
if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
(IFM_ACTIVE | IFM_AVALID)) {
@@ -358,6 +361,55 @@
}
}
+ new_link = (sc->sc_flags & AXGE_FLAG_LINK) ? 1 : 0;
+ if (old_link != new_link) {
+ if (!new_link) {
+ /*
+ * MII layer reports link down. Verify by
+ * reading the PHY BMSR register directly.
+ * BMSR link status is latched-low, so read
+ * twice: first clears any stale latch,
+ * second gives current state.
+ */
+ (void)axge_read_cmd_2(sc, AXGE_ACCESS_PHY,
+ MII_BMSR, AXGE_PHY_ADDR);
+ bmsr = axge_read_cmd_2(sc, AXGE_ACCESS_PHY,
+ MII_BMSR, AXGE_PHY_ADDR);
+
+ if (bmsr & BMSR_LINK) {
+ /*
+ * PHY still has link. This is a
+ * spurious link-down event from the
+ * MII polling race (see PR 252165).
+ * Override the status and kick TX.
+ * Also restore IFM_ACTIVE so the
+ * subsequent MIIBUS_LINKCHG check in
+ * mii_phy_update sees no status
+ * change and doesn't fire.
+ */
+ device_printf(dev,
+ "spurious link down (PHY "
+ "BMSR=0x%04x link up), "
+ "overriding\n", bmsr);
+ sc->sc_flags |= AXGE_FLAG_LINK;
+ mii->mii_media_status |= IFM_ACTIVE;
+ usbd_transfer_start(
+ sc->sc_xfer[AXGE_BULK_DT_WR]);
+ goto done;
+ }
+
+ /* PHY confirms link is genuinely down. */
+ goto done;
+ }
+
+ /*
+ * Link came up. Restart the TX transfer
+ * in case it went idle while link was down.
+ */
+ usbd_transfer_start(
+ sc->sc_xfer[AXGE_BULK_DT_WR]);
+ }
+
/* Lost link, do nothing. */
if ((sc->sc_flags & AXGE_FLAG_LINK) == 0)
goto done;
@@ -402,6 +454,40 @@
AXGE_UNLOCK(sc);
}
+static void
+axge_miibus_linkchg(device_t dev)
+{
+ struct axge_softc *sc;
+ struct mii_data *mii;
+ int locked;
+ uint16_t bmsr;
+
+ sc = device_get_softc(dev);
+ mii = GET_MII(sc);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXGE_LOCK(sc);
+
+ /*
+ * This is called by the default miibus linkchg handler
+ * before it calls if_link_state_change(). If the PHY
+ * still has link but the MII layer lost IFM_ACTIVE due
+ * to the polling race (see PR 252165), restore it so the
+ * notification goes out as LINK_STATE_UP rather than DOWN.
+ */
+ if (mii != NULL && !(mii->mii_media_status & IFM_ACTIVE)) {
+ (void)axge_read_cmd_2(sc, AXGE_ACCESS_PHY,
+ MII_BMSR, AXGE_PHY_ADDR);
+ bmsr = axge_read_cmd_2(sc, AXGE_ACCESS_PHY,
+ MII_BMSR, AXGE_PHY_ADDR);
+ if (bmsr & BMSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+ }
+
+ if (!locked)
+ AXGE_UNLOCK(sc);
+}
+
static void
axge_chip_init(struct axge_softc *sc)
{

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 16, 12:13 PM (14 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29366790
Default Alt Text
D55631.id173262.diff (3 KB)

Event Timeline