summaryrefslogtreecommitdiffstats
path: root/sys
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
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')
-rw-r--r--sys/dev/re/if_re.c80
-rw-r--r--sys/pci/if_rlreg.h1
2 files changed, 77 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);
}
diff --git a/sys/pci/if_rlreg.h b/sys/pci/if_rlreg.h
index a891b60..32256ad 100644
--- a/sys/pci/if_rlreg.h
+++ b/sys/pci/if_rlreg.h
@@ -914,6 +914,7 @@ struct rl_softc {
#define RL_FLAG_MACSLEEP 0x00002000
#define RL_FLAG_WAIT_TXPOLL 0x00004000
#define RL_FLAG_CMDSTOP_WAIT_TXQ 0x00008000
+#define RL_FLAG_WOL_MANLINK 0x00010000
#define RL_FLAG_PCIE 0x40000000
#define RL_FLAG_LINK 0x80000000
};
OpenPOWER on IntegriCloud