Rework link state tracking and TX/RX MAC configuration.

o Do not report link status if driver is not running.
 o TX/RX MAC configuration should be done with resolved speed,
   duplex and flow control after establishing a link so it can't
   be done in driver initialization routine.
   Move the configuration to miibus_statchg callback which will be
   called whenever any link state change is detected.
   At this moment, flow-control is not enabled yet mainly because
   I was not able to set correct flow control parameters to
   generate TX pause frames.
 o Now TX/RX MAC is enabled only when a valid link is detected.
   Rearragnge hardware initialization routine a bit to leave
   enabling MAC to miibus_statchg callback.  In order to that,
   TX/RX DMA engine is enabled in et_init_locked().
 o Introduce ET_FLAG_LINK flag to track current link state.
 o Introduce ET_FLAG_FASTETHER flag to mark whether controller is
   fast ethernet.  This flag is checked in miibus_statchg callback
   to know whether PHY established a valid link.
 o In et_stop(), TX/RX MAC is explicitly disabled instead of
   relying on et_reset().  And move et_reset() from et_stop() to
   controller initialization.  Controler reset is not required here
   and it would also clear critial registers(i.e station address,
   RX filter configuration, WOL etc) that are required to make WOL
   work.
 o Switching to current media is done in et_init_locked() after
   setting IFF_DRV_RUNNING flag.  This should ensure reliable
   auto-negotiation/manual link establishment.
 o In et_start_locked(), check whether driver got a valid link
   before trying to send frames.
 o Remove checking a link in et_tick() as this is done by
   miibus_statchg callback.
This commit is contained in:
Pyun YongHyeon 2011-12-07 21:29:51 +00:00
parent 57979d1bd8
commit 1f009e2f39
2 changed files with 124 additions and 106 deletions

View File

@ -141,13 +141,11 @@ static int et_start_rxdma(struct et_softc *);
static int et_start_txdma(struct et_softc *);
static int et_stop_rxdma(struct et_softc *);
static int et_stop_txdma(struct et_softc *);
static int et_enable_txrx(struct et_softc *, int);
static void et_reset(struct et_softc *);
static int et_bus_config(struct et_softc *);
static void et_get_eaddr(device_t, uint8_t[]);
static void et_setmulti(struct et_softc *);
static void et_tick(void *);
static void et_setmedia(struct et_softc *);
static const struct et_dev {
uint16_t vid;
@ -296,6 +294,9 @@ et_attach(device_t dev)
goto fail;
}
if (pci_get_device(dev) == PCI_PRODUCT_LUCENT_ET1310_FAST)
sc->sc_flags |= ET_FLAG_FASTETHER;
error = et_bus_config(sc);
if (error)
goto fail;
@ -487,7 +488,89 @@ et_miibus_writereg(device_t dev, int phy, int reg, int val0)
static void
et_miibus_statchg(device_t dev)
{
et_setmedia(device_get_softc(dev));
struct et_softc *sc;
struct mii_data *mii;
struct ifnet *ifp;
uint32_t cfg1, cfg2, ctrl;
int i;
sc = device_get_softc(dev);
mii = device_get_softc(sc->sc_miibus);
ifp = sc->ifp;
if (mii == NULL || ifp == NULL ||
(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
sc->sc_flags &= ~ET_FLAG_LINK;
if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
(IFM_ACTIVE | IFM_AVALID)) {
switch (IFM_SUBTYPE(mii->mii_media_active)) {
case IFM_10_T:
case IFM_100_TX:
sc->sc_flags |= ET_FLAG_LINK;
break;
case IFM_1000_T:
if ((sc->sc_flags & ET_FLAG_FASTETHER) == 0)
sc->sc_flags |= ET_FLAG_LINK;
break;
}
}
/* XXX Stop TX/RX MAC? */
if ((sc->sc_flags & ET_FLAG_LINK) == 0)
return;
/* Program MACs with resolved speed/duplex/flow-control. */
ctrl = CSR_READ_4(sc, ET_MAC_CTRL);
ctrl &= ~(ET_MAC_CTRL_GHDX | ET_MAC_CTRL_MODE_MII);
cfg1 = CSR_READ_4(sc, ET_MAC_CFG1);
cfg1 &= ~(ET_MAC_CFG1_TXFLOW | ET_MAC_CFG1_RXFLOW |
ET_MAC_CFG1_LOOPBACK);
cfg2 = CSR_READ_4(sc, ET_MAC_CFG2);
cfg2 &= ~(ET_MAC_CFG2_MODE_MII | ET_MAC_CFG2_MODE_GMII |
ET_MAC_CFG2_FDX | ET_MAC_CFG2_BIGFRM);
cfg2 |= ET_MAC_CFG2_LENCHK | ET_MAC_CFG2_CRC | ET_MAC_CFG2_PADCRC |
((7 << ET_MAC_CFG2_PREAMBLE_LEN_SHIFT) &
ET_MAC_CFG2_PREAMBLE_LEN_MASK);
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T)
cfg2 |= ET_MAC_CFG2_MODE_GMII;
else {
cfg2 |= ET_MAC_CFG2_MODE_MII;
ctrl |= ET_MAC_CTRL_MODE_MII;
}
if (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) {
cfg2 |= ET_MAC_CFG2_FDX;
#ifdef notyet
if (IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE)
cfg1 |= ET_MAC_CFG1_TXFLOW;
if (IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE)
cfg1 |= ET_MAC_CFG1_RXFLOW;
#endif
} else
ctrl |= ET_MAC_CTRL_GHDX;
CSR_WRITE_4(sc, ET_MAC_CTRL, ctrl);
CSR_WRITE_4(sc, ET_MAC_CFG2, cfg2);
cfg1 |= ET_MAC_CFG1_TXEN | ET_MAC_CFG1_RXEN;
CSR_WRITE_4(sc, ET_MAC_CFG1, cfg1);
#define NRETRY 50
for (i = 0; i < NRETRY; ++i) {
cfg1 = CSR_READ_4(sc, ET_MAC_CFG1);
if ((cfg1 & (ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN)) ==
(ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN))
break;
DELAY(100);
}
if (i == NRETRY)
if_printf(ifp, "can't enable RX/TX\n");
sc->sc_flags |= ET_FLAG_TXRX_ENABLED;
#undef NRETRY
}
static int
@ -518,10 +601,17 @@ et_ifmedia_upd(struct ifnet *ifp)
static void
et_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct et_softc *sc = ifp->if_softc;
struct mii_data *mii = device_get_softc(sc->sc_miibus);
struct et_softc *sc;
struct mii_data *mii;
sc = ifp->if_softc;
ET_LOCK(sc);
if ((ifp->if_flags & IFF_UP) == 0) {
ET_UNLOCK(sc);
return;
}
mii = device_get_softc(sc->sc_miibus);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
@ -539,14 +629,16 @@ et_stop(struct et_softc *sc)
/* Disable interrupts. */
CSR_WRITE_4(sc, ET_INTR_MASK, 0xffffffff);
CSR_WRITE_4(sc, ET_MAC_CFG1, CSR_READ_4(sc, ET_MAC_CFG1) & ~(
ET_MAC_CFG1_TXEN | ET_MAC_CFG1_RXEN));
DELAY(100);
et_stop_rxdma(sc);
et_stop_txdma(sc);
et_free_tx_ring(sc);
et_free_rx_ring(sc);
et_reset(sc);
sc->sc_tx = 0;
sc->sc_tx_intr = 0;
sc->sc_flags &= ~ET_FLAG_TXRX_ENABLED;
@ -1104,6 +1196,7 @@ et_init_locked(struct et_softc *sc)
return;
et_stop(sc);
et_reset(sc);
et_init_tx_ring(sc);
error = et_init_rx_ring(sc);
@ -1112,22 +1205,33 @@ et_init_locked(struct et_softc *sc)
error = et_chip_init(sc);
if (error)
goto back;
goto fail;
error = et_enable_txrx(sc, 1);
/*
* Start TX/RX DMA engine
*/
error = et_start_rxdma(sc);
if (error)
goto back;
return;
error = et_start_txdma(sc);
if (error)
return;
/* Enable interrupts. */
CSR_WRITE_4(sc, ET_INTR_MASK, ~ET_INTRS);
callout_reset(&sc->sc_tick, hz, et_tick, sc);
CSR_WRITE_4(sc, ET_TIMER, sc->sc_timer);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
back:
sc->sc_flags &= ~ET_FLAG_LINK;
et_ifmedia_upd_locked(ifp);
callout_reset(&sc->sc_tick, hz, et_tick, sc);
fail:
if (error)
et_stop(sc);
}
@ -1239,10 +1343,10 @@ et_start_locked(struct ifnet *ifp)
sc = ifp->if_softc;
ET_LOCK_ASSERT(sc);
if ((sc->sc_flags & ET_FLAG_TXRX_ENABLED) == 0)
return;
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING)
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING ||
(sc->sc_flags & (ET_FLAG_LINK | ET_FLAG_TXRX_ENABLED)) !=
(ET_FLAG_LINK | ET_FLAG_TXRX_ENABLED))
return;
/*
@ -1870,56 +1974,6 @@ et_start_txdma(struct et_softc *sc)
return (0);
}
static int
et_enable_txrx(struct et_softc *sc, int media_upd)
{
struct ifnet *ifp = sc->ifp;
uint32_t val;
int i, error;
val = CSR_READ_4(sc, ET_MAC_CFG1);
val |= ET_MAC_CFG1_TXEN | ET_MAC_CFG1_RXEN;
val &= ~(ET_MAC_CFG1_TXFLOW | ET_MAC_CFG1_RXFLOW |
ET_MAC_CFG1_LOOPBACK);
CSR_WRITE_4(sc, ET_MAC_CFG1, val);
if (media_upd)
et_ifmedia_upd_locked(ifp);
else
et_setmedia(sc);
#define NRETRY 50
for (i = 0; i < NRETRY; ++i) {
val = CSR_READ_4(sc, ET_MAC_CFG1);
if ((val & (ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN)) ==
(ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN))
break;
DELAY(100);
}
if (i == NRETRY) {
if_printf(ifp, "can't enable RX/TX\n");
return (0);
}
sc->sc_flags |= ET_FLAG_TXRX_ENABLED;
#undef NRETRY
/*
* Start TX/RX DMA engine
*/
error = et_start_rxdma(sc);
if (error)
return (error);
error = et_start_txdma(sc);
if (error)
return (error);
return (0);
}
static void
et_rxeof(struct et_softc *sc)
{
@ -2192,6 +2246,7 @@ et_txeof(struct et_softc *sc)
if (tbd->tbd_used + ET_NSEG_SPARE < ET_TX_NDESC)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
}
static void
et_tick(void *xsc)
{
@ -2204,13 +2259,6 @@ et_tick(void *xsc)
mii = device_get_softc(sc->sc_miibus);
mii_tick(mii);
if ((sc->sc_flags & ET_FLAG_TXRX_ENABLED) == 0 &&
(mii->mii_media_status & IFM_ACTIVE) &&
IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
if_printf(ifp, "Link up, enable TX/RX\n");
if (et_enable_txrx(sc, 0) == 0)
et_start_locked(ifp);
}
if (et_watchdog(sc) == EJUSTRETURN)
return;
callout_reset(&sc->sc_tick, hz, et_tick, sc);
@ -2398,38 +2446,6 @@ et_sysctl_rx_intr_delay(SYSCTL_HANDLER_ARGS)
return (error);
}
static void
et_setmedia(struct et_softc *sc)
{
struct mii_data *mii = device_get_softc(sc->sc_miibus);
uint32_t cfg2, ctrl;
cfg2 = CSR_READ_4(sc, ET_MAC_CFG2);
cfg2 &= ~(ET_MAC_CFG2_MODE_MII | ET_MAC_CFG2_MODE_GMII |
ET_MAC_CFG2_FDX | ET_MAC_CFG2_BIGFRM);
cfg2 |= ET_MAC_CFG2_LENCHK | ET_MAC_CFG2_CRC | ET_MAC_CFG2_PADCRC |
((7 << ET_MAC_CFG2_PREAMBLE_LEN_SHIFT) &
ET_MAC_CFG2_PREAMBLE_LEN_MASK);
ctrl = CSR_READ_4(sc, ET_MAC_CTRL);
ctrl &= ~(ET_MAC_CTRL_GHDX | ET_MAC_CTRL_MODE_MII);
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
cfg2 |= ET_MAC_CFG2_MODE_GMII;
} else {
cfg2 |= ET_MAC_CFG2_MODE_MII;
ctrl |= ET_MAC_CTRL_MODE_MII;
}
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
cfg2 |= ET_MAC_CFG2_FDX;
else
ctrl |= ET_MAC_CTRL_GHDX;
CSR_WRITE_4(sc, ET_MAC_CTRL, ctrl);
CSR_WRITE_4(sc, ET_MAC_CFG2, cfg2);
}
static int
et_suspend(device_t dev)
{

View File

@ -289,7 +289,9 @@ struct et_softc {
#define ET_FLAG_PCIE 0x0001
#define ET_FLAG_MSI 0x0002
#define ET_FLAG_FASTETHER 0x0004
#define ET_FLAG_TXRX_ENABLED 0x0100
#define ET_FLAG_JUMBO 0x0200
#define ET_FLAG_LINK 0x8000
#endif /* !_IF_ETVAR_H */