summaryrefslogtreecommitdiffstats
path: root/sys/dev/re
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2011-11-23 23:29:18 +0000
committeryongari <yongari@FreeBSD.org>2011-11-23 23:29:18 +0000
commitbf1680595f0cbf23350569e351d6b1eb3434b3f6 (patch)
tree139086061f0f9b0146e722ff915bf9c3e8601cb1 /sys/dev/re
parent59c40435df4cc5e4b692d0b1588298e0754c3342 (diff)
downloadFreeBSD-src-bf1680595f0cbf23350569e351d6b1eb3434b3f6.zip
FreeBSD-src-bf1680595f0cbf23350569e351d6b1eb3434b3f6.tar.gz
To save more power, switch to 10/100Mbps link when controller is
put into suspend/shutdown. Old PCI controllers performed that operation in firmware but for RTL8111C or newer controllers, it's responsibility of driver. It's not clear whether the firmware of RTL8111B still downgrades its speed to 10/100Mbps so leave it as it was.
Diffstat (limited to 'sys/dev/re')
-rw-r--r--sys/dev/re/if_re.c80
1 files changed, 76 insertions, 4 deletions
diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c
index ad10400..da90951 100644
--- a/sys/dev/re/if_re.c
+++ b/sys/dev/re/if_re.c
@@ -294,6 +294,7 @@ static void re_set_rxmode (struct rl_softc *);
static void re_reset (struct rl_softc *);
static void re_setwol (struct rl_softc *);
static void re_clrwol (struct rl_softc *);
+static void re_set_linkspeed (struct rl_softc *);
#ifdef RE_DIAG
static int re_diag (struct rl_softc *);
@@ -1431,17 +1432,18 @@ re_attach(device_t dev)
case RL_HWREV_8168D:
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
- RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2;
+ RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 | RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8168DP:
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_AUTOPAD |
- RL_FLAG_JUMBOV2 | RL_FLAG_WAIT_TXPOLL;
+ RL_FLAG_JUMBOV2 | RL_FLAG_WAIT_TXPOLL | RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8168E:
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
- RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2;
+ RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
+ RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8168E_VL:
case RL_HWREV_8168F:
@@ -1449,7 +1451,7 @@ re_attach(device_t dev)
sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
- RL_FLAG_CMDSTOP_WAIT_TXQ;
+ RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK;
break;
case RL_HWREV_8169_8110SB:
case RL_HWREV_8169_8110SBL:
@@ -3627,6 +3629,74 @@ re_shutdown(device_t dev)
}
static void
+re_set_linkspeed(struct rl_softc *sc)
+{
+ struct mii_softc *miisc;
+ struct mii_data *mii;
+ int aneg, i, phyno;
+
+ RL_LOCK_ASSERT(sc);
+
+ mii = device_get_softc(sc->rl_miibus);
+ mii_pollstat(mii);
+ aneg = 0;
+ 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:
+ return;
+ case IFM_1000_T:
+ aneg++;
+ break;
+ default:
+ break;
+ }
+ }
+ miisc = LIST_FIRST(&mii->mii_phys);
+ phyno = miisc->mii_phy;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ re_miibus_writereg(sc->rl_dev, phyno, MII_100T2CR, 0);
+ re_miibus_writereg(sc->rl_dev, phyno,
+ MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA);
+ re_miibus_writereg(sc->rl_dev, phyno,
+ MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+ DELAY(1000);
+ if (aneg != 0) {
+ /*
+ * Poll link state until re(4) get a 10/100Mbps link.
+ */
+ for (i = 0; i < MII_ANEGTICKS_GIGE; i++) {
+ mii_pollstat(mii);
+ 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:
+ return;
+ default:
+ break;
+ }
+ }
+ RL_UNLOCK(sc);
+ pause("relnk", hz);
+ RL_LOCK(sc);
+ }
+ if (i == MII_ANEGTICKS_GIGE)
+ device_printf(sc->rl_dev,
+ "establishing a link failed, WOL may not work!");
+ }
+ /*
+ * No link, force MAC to have 100Mbps, full-duplex link.
+ * MAC does not require reprogramming on resolved speed/duplex,
+ * so this is just for completeness.
+ */
+ mii->mii_media_status = IFM_AVALID | IFM_ACTIVE;
+ mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
+}
+
+static void
re_setwol(struct rl_softc *sc)
{
struct ifnet *ifp;
@@ -3648,6 +3718,8 @@ re_setwol(struct rl_softc *sc)
}
if ((ifp->if_capenable & IFCAP_WOL) != 0) {
re_set_rxmode(sc);
+ if ((sc->rl_flags & RL_FLAG_WOL_MANLINK) != 0)
+ re_set_linkspeed(sc);
if ((sc->rl_flags & RL_FLAG_WOLRXENB) != 0)
CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RX_ENB);
}
OpenPOWER on IntegriCloud