mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
Implement WOL. WOL is supported on RTL8139B or newer controllers.
PR: kern/148013
This commit is contained in:
parent
33ac3bc5a2
commit
6c9a12e7b0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=210244
141
sys/pci/if_rl.c
141
sys/pci/if_rl.c
@ -222,6 +222,8 @@ static int rl_suspend(device_t);
|
|||||||
static void rl_tick(void *);
|
static void rl_tick(void *);
|
||||||
static void rl_txeof(struct rl_softc *);
|
static void rl_txeof(struct rl_softc *);
|
||||||
static void rl_watchdog(struct rl_softc *);
|
static void rl_watchdog(struct rl_softc *);
|
||||||
|
static void rl_setwol(struct rl_softc *);
|
||||||
|
static void rl_clrwol(struct rl_softc *);
|
||||||
|
|
||||||
#ifdef RL_USEIOSPACE
|
#ifdef RL_USEIOSPACE
|
||||||
#define RL_RES SYS_RES_IOPORT
|
#define RL_RES SYS_RES_IOPORT
|
||||||
@ -803,7 +805,7 @@ rl_attach(device_t dev)
|
|||||||
struct rl_type *t;
|
struct rl_type *t;
|
||||||
struct sysctl_ctx_list *ctx;
|
struct sysctl_ctx_list *ctx;
|
||||||
struct sysctl_oid_list *children;
|
struct sysctl_oid_list *children;
|
||||||
int error = 0, i, rid;
|
int error = 0, hwrev, i, pmc, rid;
|
||||||
int unit;
|
int unit;
|
||||||
uint16_t rl_did = 0;
|
uint16_t rl_did = 0;
|
||||||
char tn[32];
|
char tn[32];
|
||||||
@ -938,6 +940,25 @@ rl_attach(device_t dev)
|
|||||||
ifp->if_start = rl_start;
|
ifp->if_start = rl_start;
|
||||||
ifp->if_init = rl_init;
|
ifp->if_init = rl_init;
|
||||||
ifp->if_capabilities = IFCAP_VLAN_MTU;
|
ifp->if_capabilities = IFCAP_VLAN_MTU;
|
||||||
|
/* Check WOL for RTL8139B or newer controllers. */
|
||||||
|
if (sc->rl_type == RL_8139 &&
|
||||||
|
pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
|
||||||
|
hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
|
||||||
|
switch (hwrev) {
|
||||||
|
case RL_HWREV_8139B:
|
||||||
|
case RL_HWREV_8130:
|
||||||
|
case RL_HWREV_8139C:
|
||||||
|
case RL_HWREV_8139D:
|
||||||
|
case RL_HWREV_8101:
|
||||||
|
case RL_HWREV_8100:
|
||||||
|
ifp->if_capabilities |= IFCAP_WOL;
|
||||||
|
/* Disable WOL. */
|
||||||
|
rl_clrwol(sc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
ifp->if_capenable = ifp->if_capabilities;
|
ifp->if_capenable = ifp->if_capabilities;
|
||||||
#ifdef DEVICE_POLLING
|
#ifdef DEVICE_POLLING
|
||||||
ifp->if_capabilities |= IFCAP_POLLING;
|
ifp->if_capabilities |= IFCAP_POLLING;
|
||||||
@ -1926,7 +1947,7 @@ rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|||||||
struct ifreq *ifr = (struct ifreq *)data;
|
struct ifreq *ifr = (struct ifreq *)data;
|
||||||
struct mii_data *mii;
|
struct mii_data *mii;
|
||||||
struct rl_softc *sc = ifp->if_softc;
|
struct rl_softc *sc = ifp->if_softc;
|
||||||
int error = 0;
|
int error = 0, mask;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case SIOCSIFFLAGS:
|
case SIOCSIFFLAGS:
|
||||||
@ -1953,6 +1974,7 @@ rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|||||||
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
|
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
|
||||||
break;
|
break;
|
||||||
case SIOCSIFCAP:
|
case SIOCSIFCAP:
|
||||||
|
mask = ifr->ifr_reqcap ^ ifp->if_capenable;
|
||||||
#ifdef DEVICE_POLLING
|
#ifdef DEVICE_POLLING
|
||||||
if (ifr->ifr_reqcap & IFCAP_POLLING &&
|
if (ifr->ifr_reqcap & IFCAP_POLLING &&
|
||||||
!(ifp->if_capenable & IFCAP_POLLING)) {
|
!(ifp->if_capenable & IFCAP_POLLING)) {
|
||||||
@ -1978,6 +2000,15 @@ rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
#endif /* DEVICE_POLLING */
|
#endif /* DEVICE_POLLING */
|
||||||
|
if ((mask & IFCAP_WOL) != 0 &&
|
||||||
|
(ifp->if_capabilities & IFCAP_WOL) != 0) {
|
||||||
|
if ((mask & IFCAP_WOL_UCAST) != 0)
|
||||||
|
ifp->if_capenable ^= IFCAP_WOL_UCAST;
|
||||||
|
if ((mask & IFCAP_WOL_MCAST) != 0)
|
||||||
|
ifp->if_capenable ^= IFCAP_WOL_MCAST;
|
||||||
|
if ((mask & IFCAP_WOL_MAGIC) != 0)
|
||||||
|
ifp->if_capenable ^= IFCAP_WOL_MAGIC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = ether_ioctl(ifp, command, data);
|
error = ether_ioctl(ifp, command, data);
|
||||||
@ -2066,6 +2097,7 @@ rl_suspend(device_t dev)
|
|||||||
|
|
||||||
RL_LOCK(sc);
|
RL_LOCK(sc);
|
||||||
rl_stop(sc);
|
rl_stop(sc);
|
||||||
|
rl_setwol(sc);
|
||||||
sc->suspended = 1;
|
sc->suspended = 1;
|
||||||
RL_UNLOCK(sc);
|
RL_UNLOCK(sc);
|
||||||
|
|
||||||
@ -2082,12 +2114,31 @@ rl_resume(device_t dev)
|
|||||||
{
|
{
|
||||||
struct rl_softc *sc;
|
struct rl_softc *sc;
|
||||||
struct ifnet *ifp;
|
struct ifnet *ifp;
|
||||||
|
int pmc;
|
||||||
|
uint16_t pmstat;
|
||||||
|
|
||||||
sc = device_get_softc(dev);
|
sc = device_get_softc(dev);
|
||||||
ifp = sc->rl_ifp;
|
ifp = sc->rl_ifp;
|
||||||
|
|
||||||
RL_LOCK(sc);
|
RL_LOCK(sc);
|
||||||
|
|
||||||
|
if ((ifp->if_capabilities & IFCAP_WOL) != 0 &&
|
||||||
|
pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
|
||||||
|
/* Disable PME and clear PME status. */
|
||||||
|
pmstat = pci_read_config(sc->rl_dev,
|
||||||
|
pmc + PCIR_POWER_STATUS, 2);
|
||||||
|
if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) {
|
||||||
|
pmstat &= ~PCIM_PSTAT_PMEENABLE;
|
||||||
|
pci_write_config(sc->rl_dev,
|
||||||
|
pmc + PCIR_POWER_STATUS, pmstat, 2);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Clear WOL matching such that normal Rx filtering
|
||||||
|
* wouldn't interfere with WOL patterns.
|
||||||
|
*/
|
||||||
|
rl_clrwol(sc);
|
||||||
|
}
|
||||||
|
|
||||||
/* reinitialize interface if necessary */
|
/* reinitialize interface if necessary */
|
||||||
if (ifp->if_flags & IFF_UP)
|
if (ifp->if_flags & IFF_UP)
|
||||||
rl_init_locked(sc);
|
rl_init_locked(sc);
|
||||||
@ -2112,7 +2163,93 @@ rl_shutdown(device_t dev)
|
|||||||
|
|
||||||
RL_LOCK(sc);
|
RL_LOCK(sc);
|
||||||
rl_stop(sc);
|
rl_stop(sc);
|
||||||
|
/*
|
||||||
|
* Mark interface as down since otherwise we will panic if
|
||||||
|
* interrupt comes in later on, which can happen in some
|
||||||
|
* cases.
|
||||||
|
*/
|
||||||
|
sc->rl_ifp->if_flags &= ~IFF_UP;
|
||||||
|
rl_setwol(sc);
|
||||||
RL_UNLOCK(sc);
|
RL_UNLOCK(sc);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rl_setwol(struct rl_softc *sc)
|
||||||
|
{
|
||||||
|
struct ifnet *ifp;
|
||||||
|
int pmc;
|
||||||
|
uint16_t pmstat;
|
||||||
|
uint8_t v;
|
||||||
|
|
||||||
|
RL_LOCK_ASSERT(sc);
|
||||||
|
|
||||||
|
ifp = sc->rl_ifp;
|
||||||
|
if ((ifp->if_capabilities & IFCAP_WOL) == 0)
|
||||||
|
return;
|
||||||
|
if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Enable config register write. */
|
||||||
|
CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
|
||||||
|
|
||||||
|
/* Enable PME. */
|
||||||
|
v = CSR_READ_1(sc, RL_CFG1);
|
||||||
|
v &= ~RL_CFG1_PME;
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL) != 0)
|
||||||
|
v |= RL_CFG1_PME;
|
||||||
|
CSR_WRITE_1(sc, RL_CFG1, v);
|
||||||
|
|
||||||
|
v = CSR_READ_1(sc, RL_CFG3);
|
||||||
|
v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
|
||||||
|
v |= RL_CFG3_WOL_MAGIC;
|
||||||
|
CSR_WRITE_1(sc, RL_CFG3, v);
|
||||||
|
|
||||||
|
/* Config register write done. */
|
||||||
|
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
|
||||||
|
|
||||||
|
v = CSR_READ_1(sc, RL_CFG5);
|
||||||
|
v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
|
||||||
|
v &= ~RL_CFG5_WOL_LANWAKE;
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0)
|
||||||
|
v |= RL_CFG5_WOL_UCAST;
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
|
||||||
|
v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST;
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL) != 0)
|
||||||
|
v |= RL_CFG5_WOL_LANWAKE;
|
||||||
|
CSR_WRITE_1(sc, RL_CFG5, v);
|
||||||
|
/* Request PME if WOL is requested. */
|
||||||
|
pmstat = pci_read_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, 2);
|
||||||
|
pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
|
||||||
|
if ((ifp->if_capenable & IFCAP_WOL) != 0)
|
||||||
|
pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
|
||||||
|
pci_write_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rl_clrwol(struct rl_softc *sc)
|
||||||
|
{
|
||||||
|
struct ifnet *ifp;
|
||||||
|
uint8_t v;
|
||||||
|
|
||||||
|
ifp = sc->rl_ifp;
|
||||||
|
if ((ifp->if_capabilities & IFCAP_WOL) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Enable config register write. */
|
||||||
|
CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
|
||||||
|
|
||||||
|
v = CSR_READ_1(sc, RL_CFG3);
|
||||||
|
v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
|
||||||
|
CSR_WRITE_1(sc, RL_CFG3, v);
|
||||||
|
|
||||||
|
/* Config register write done. */
|
||||||
|
CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
|
||||||
|
|
||||||
|
v = CSR_READ_1(sc, RL_CFG5);
|
||||||
|
v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
|
||||||
|
v &= ~RL_CFG5_WOL_LANWAKE;
|
||||||
|
CSR_WRITE_1(sc, RL_CFG5, v);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user