summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2011-11-22 20:45:09 +0000
committeryongari <yongari@FreeBSD.org>2011-11-22 20:45:09 +0000
commit6cf42ed3b9692fe7b447e2fecdc982f5952fb142 (patch)
treec01b7dedb17a8e15c4877548ee24b55a795b89e1
parentbd0ca7a0ba7d398a2d0d0faf886ceb00713cddb7 (diff)
downloadFreeBSD-src-6cf42ed3b9692fe7b447e2fecdc982f5952fb142.zip
FreeBSD-src-6cf42ed3b9692fe7b447e2fecdc982f5952fb142.tar.gz
Rework link establishment and link state detection logic.
- Remove MIIBUS statchg callback and program VGE_DIAGCTL before initiating link establishment. Previously driver used to program VGE_DIAGCTL after getting a link in statchg callback. It seems the VGE_DIAGCTL register works like a kind of MII register such that it requires setting a 'to be' mode in advance rather than relying on resolved speed/duplex of established link. This means the statchg callback is not needed in driver. In addition, if there was no link at the time of media change, this was not called at all. - Introduce vge_ifmedia_upd_locked() to change current media to configured one. Actual media change is performed only after PHY reset and VGE_DIAGCTL setup. - In WOL configuration, make sure to clear forced mode such that controller can rely on auto-negotiation. - Unlike most other drivers that use miibus(4), vge(4) used controller's auto-polling feature for link state tracking via interrupt. This came from controller's inefficient mechanism to access MII registers. On link state change interrupt, vge(4) used to get current link state with series of MII register accesses. Because vge(4) already enabled auto polling, read PHY status register to resolved speed/duplex/flow control parameters. vge(4) still does not drive MII_TICK to reduce number of MII register accesses which in turn means the driver does not know the status of auto-negotiation. This was a one of long standing issue of vge(4). Probably driver may be able to implement a timer that keeps track of auto-negotiation state and restart auto-negotiation when driver couldn't establish a link within a specified period. However the controller does not provide a reliable way to detect auto-negotiation failure so I'm not sure whether it's worth to implement it in driver. Alternatively driver can completely disable MII auto-polling and let miibus(4) poll link state by driving MII_TICK. This may reduce unnecessary overhead of stopping/restarting MII auto-polling of controller. Unfortunately it was known that some variants of controller does not work correctly if MII auto-polling is disabled.
-rw-r--r--sys/dev/vge/if_vge.c62
1 files changed, 44 insertions, 18 deletions
diff --git a/sys/dev/vge/if_vge.c b/sys/dev/vge/if_vge.c
index c757261..720c320 100644
--- a/sys/dev/vge/if_vge.c
+++ b/sys/dev/vge/if_vge.c
@@ -173,6 +173,7 @@ static __inline void
static void vge_freebufs(struct vge_softc *);
static void vge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
static int vge_ifmedia_upd(struct ifnet *);
+static int vge_ifmedia_upd_locked(struct vge_softc *);
static void vge_init(void *);
static void vge_init_locked(struct vge_softc *);
static void vge_intr(void *);
@@ -180,7 +181,6 @@ static void vge_intr_holdoff(struct vge_softc *);
static int vge_ioctl(struct ifnet *, u_long, caddr_t);
static void vge_link_statchg(void *);
static int vge_miibus_readreg(device_t, int, int);
-static void vge_miibus_statchg(device_t);
static int vge_miibus_writereg(device_t, int, int, int);
static void vge_miipoll_start(struct vge_softc *);
static void vge_miipoll_stop(struct vge_softc *);
@@ -190,6 +190,7 @@ static void vge_reset(struct vge_softc *);
static int vge_rx_list_init(struct vge_softc *);
static int vge_rxeof(struct vge_softc *, int);
static void vge_rxfilter(struct vge_softc *);
+static void vge_setmedia(struct vge_softc *);
static void vge_setvlan(struct vge_softc *);
static void vge_setwol(struct vge_softc *);
static void vge_start(struct ifnet *);
@@ -218,7 +219,6 @@ static device_method_t vge_methods[] = {
/* MII interface */
DEVMETHOD(miibus_readreg, vge_miibus_readreg),
DEVMETHOD(miibus_writereg, vge_miibus_writereg),
- DEVMETHOD(miibus_statchg, vge_miibus_statchg),
{ 0, 0 }
};
@@ -1661,30 +1661,41 @@ vge_link_statchg(void *xsc)
{
struct vge_softc *sc;
struct ifnet *ifp;
- struct mii_data *mii;
+ uint8_t physts;
sc = xsc;
ifp = sc->vge_ifp;
VGE_LOCK_ASSERT(sc);
- mii = device_get_softc(sc->vge_miibus);
- mii_pollstat(mii);
- if ((sc->vge_flags & VGE_FLAG_LINK) != 0) {
- if (!(mii->mii_media_status & IFM_ACTIVE)) {
+ physts = CSR_READ_1(sc, VGE_PHYSTS0);
+ if ((physts & VGE_PHYSTS_RESETSTS) == 0) {
+ if ((physts & VGE_PHYSTS_LINK) == 0) {
sc->vge_flags &= ~VGE_FLAG_LINK;
if_link_state_change(sc->vge_ifp,
LINK_STATE_DOWN);
- }
- } else {
- if (mii->mii_media_status & IFM_ACTIVE &&
- IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ } else {
sc->vge_flags |= VGE_FLAG_LINK;
if_link_state_change(sc->vge_ifp,
LINK_STATE_UP);
+ CSR_WRITE_1(sc, VGE_CRC2, VGE_CR2_FDX_TXFLOWCTL_ENABLE |
+ VGE_CR2_FDX_RXFLOWCTL_ENABLE);
+ if ((physts & VGE_PHYSTS_FDX) != 0) {
+ if ((physts & VGE_PHYSTS_TXFLOWCAP) != 0)
+ CSR_WRITE_1(sc, VGE_CRS2,
+ VGE_CR2_FDX_TXFLOWCTL_ENABLE);
+ if ((physts & VGE_PHYSTS_RXFLOWCAP) != 0)
+ CSR_WRITE_1(sc, VGE_CRS2,
+ VGE_CR2_FDX_RXFLOWCTL_ENABLE);
+ }
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
vge_start_locked(ifp);
}
}
+ /*
+ * Restart MII auto-polling because link state change interrupt
+ * will disable it.
+ */
+ vge_miipoll_start(sc);
}
#ifdef DEVICE_POLLING
@@ -2131,7 +2142,7 @@ vge_init_locked(struct vge_softc *sc)
CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
sc->vge_flags &= ~VGE_FLAG_LINK;
- mii_mediachg(mii);
+ vge_ifmedia_upd_locked(sc);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
@@ -2145,14 +2156,28 @@ static int
vge_ifmedia_upd(struct ifnet *ifp)
{
struct vge_softc *sc;
- struct mii_data *mii;
int error;
sc = ifp->if_softc;
VGE_LOCK(sc);
+ error = vge_ifmedia_upd_locked(sc);
+ VGE_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+vge_ifmedia_upd_locked(struct vge_softc *sc)
+{
+ struct mii_data *mii;
+ struct mii_softc *miisc;
+ int error;
+
mii = device_get_softc(sc->vge_miibus);
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ vge_setmedia(sc);
error = mii_mediachg(mii);
- VGE_UNLOCK(sc);
return (error);
}
@@ -2181,13 +2206,11 @@ vge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
}
static void
-vge_miibus_statchg(device_t dev)
+vge_setmedia(struct vge_softc *sc)
{
- struct vge_softc *sc;
struct mii_data *mii;
struct ifmedia_entry *ife;
- sc = device_get_softc(dev);
mii = device_get_softc(sc->vge_miibus);
ife = mii->mii_media.ifm_cur;
@@ -2221,7 +2244,7 @@ vge_miibus_statchg(device_t dev)
}
break;
default:
- device_printf(dev, "unknown media type: %x\n",
+ device_printf(sc->vge_dev, "unknown media type: %x\n",
IFM_SUBTYPE(ife->ifm_media));
break;
}
@@ -2774,6 +2797,9 @@ vge_setlinkspeed(struct vge_softc *sc)
break;
}
}
+ /* Clear forced MAC speed/duplex configuration. */
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_MACFORCE);
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_FDXFORCE);
vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_100T2CR, 0);
vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_ANAR,
ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA);
OpenPOWER on IntegriCloud