From 7268ae80ca51c94f4737862e110e7d42eadae227 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Sat, 4 Sep 2021 06:31:32 -0700 Subject: [PATCH] bge(4): Add WOL support With this patch bge(4) receives WOL support. Like other NICs in FreeBSD when any of the WOL flags is set the NIC is set to WOL upon NIC shutdown. PR: 218579, 171744, 177184, 184718 Reported by: pozzugno@gmail.com Reported by: Stephen Workman Reported by: Hielke Christian Braun Reported by: Simon Magrin Reviewed by: Tested by: Peter Libassi on non-Acer machine MFC after: 2 weeks Relnotes: yes Differential Revision: (cherry picked from commit 17c3c4975e266efbd98617c58d8293998fdd12df) --- sys/dev/bge/if_bge.c | 113 ++++++++++++++++++++++++++++++++++++++-- sys/dev/bge/if_bgereg.h | 3 +- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index 7a35aa518b8c..4c50765cc048 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -1,4 +1,4 @@ -/*- +/* * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2001 Wind River Systems @@ -479,6 +479,8 @@ static void bge_sig_pre_reset(struct bge_softc *, int); static void bge_stop_fw(struct bge_softc *); static int bge_reset(struct bge_softc *); static void bge_link_upd(struct bge_softc *); +static void bge_setwol(struct bge_softc *); +static void bge_clrwol(struct bge_softc *); static void bge_ape_lock_init(struct bge_softc *); static void bge_ape_read_fw_ver(struct bge_softc *); @@ -890,6 +892,7 @@ bge_ape_send_event(struct bge_softc *sc, uint32_t event) static void bge_ape_driver_state_change(struct bge_softc *sc, int kind) { + struct ifnet *ifp; uint32_t apedata, event; if ((sc->bge_mfw_flags & BGE_MFW_ON_APE) == 0) @@ -922,8 +925,24 @@ bge_ape_driver_state_change(struct bge_softc *sc, int kind) event = BGE_APE_EVENT_STATUS_STATE_START; break; case BGE_RESET_SHUTDOWN: - APE_WRITE_4(sc, BGE_APE_HOST_DRVR_STATE, - BGE_APE_HOST_DRVR_STATE_UNLOAD); + /* XXX Needs rewording + * With the interface we are currently using, + * APE does not track driver state. Wiping + * out the HOST SEGMENT SIGNATURE forces + * the APE to assume OS absent status. + */ + APE_WRITE_4(sc, BGE_APE_HOST_SEG_SIG, 0); + + ifp = sc->bge_ifp; + if ((if_getcapenable(ifp) & IFCAP_WOL) != 0) { + APE_WRITE_4(sc, BGE_APE_HOST_WOL_SPEED, + BGE_APE_HOST_WOL_SPEED_AUTO); + APE_WRITE_4(sc, BGE_APE_HOST_DRVR_STATE, + BGE_APE_HOST_DRVR_STATE_WOL); + } else { + APE_WRITE_4(sc, BGE_APE_HOST_DRVR_STATE, + BGE_APE_HOST_DRVR_STATE_UNLOAD); + } event = BGE_APE_EVENT_STATUS_STATE_UNLOAD; break; case BGE_RESET_SUSPEND: @@ -3730,7 +3749,7 @@ bge_attach(device_t dev) if_setsendqready(ifp); if_sethwassist(ifp, sc->bge_csum_features); if_setcapabilities(ifp, IFCAP_HWCSUM | IFCAP_VLAN_HWTAGGING | - IFCAP_VLAN_MTU); + IFCAP_VLAN_MTU | IFCAP_WOL_MAGIC); if ((sc->bge_flags & (BGE_FLAG_TSO | BGE_FLAG_TSO3)) != 0) { if_sethwassistbits(ifp, CSUM_TSO, 0); if_setcapabilitiesbit(ifp, IFCAP_TSO4 | IFCAP_VLAN_HWTSO, 0); @@ -3738,6 +3757,8 @@ bge_attach(device_t dev) #ifdef IFCAP_VLAN_HWCSUM if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWCSUM, 0); #endif + if (pci_find_cap(dev, PCIY_PMG, ®) == 0) + if_setcapabilitiesbit(ifp, IFCAP_WOL_MAGIC, 0); if_setcapenable(ifp, if_getcapabilities(ifp)); #ifdef DEVICE_POLLING if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0); @@ -3923,6 +3944,9 @@ bge_attach(device_t dev) device_printf(sc->bge_dev, "couldn't set up irq\n"); goto fail; } + BGE_LOCK(sc); + bge_clrwol(sc); + BGE_UNLOCK(sc); /* Attach driver debugnet methods. */ DEBUGNET_SET(ifp, bge); @@ -3950,6 +3974,7 @@ bge_detach(device_t dev) if (device_is_attached(dev)) { ether_ifdetach(ifp); BGE_LOCK(sc); + bge_setwol(sc); bge_stop(sc); BGE_UNLOCK(sc); callout_drain(&sc->bge_stat_ch); @@ -4137,7 +4162,8 @@ bge_reset(struct bge_softc *sc) /* Reset some of the PCI state that got zapped by reset. */ pci_write_config(dev, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_INDIRECT_ACCESS | BGE_PCIMISCCTL_MASK_PCI_INTR | - BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW, 4); + BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW | + BGE_PCIMISCCTL_CLOCKCTL_RW, 4); val = BGE_PCISTATE_ROM_ENABLE | BGE_PCISTATE_ROM_RETRY_ENABLE; if (sc->bge_chipid == BGE_CHIPID_BCM5704_A0 && (sc->bge_flags & BGE_FLAG_PCIX) != 0) @@ -5843,6 +5869,9 @@ bge_ioctl(if_t ifp, u_long command, caddr_t data) } } #endif + if ((mask & IFCAP_WOL_MAGIC) != 0 && + (if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) != 0) + if_togglecapenable(ifp, IFCAP_WOL_MAGIC); if ((mask & IFCAP_TXCSUM) != 0 && (if_getcapabilities(ifp) & IFCAP_TXCSUM) != 0) { if_togglecapenable(ifp, IFCAP_TXCSUM); @@ -6070,6 +6099,7 @@ bge_shutdown(device_t dev) sc = device_get_softc(dev); BGE_LOCK(sc); + bge_setwol(sc); bge_stop(sc); BGE_UNLOCK(sc); @@ -6083,6 +6113,7 @@ bge_suspend(device_t dev) sc = device_get_softc(dev); BGE_LOCK(sc); + bge_setwol(sc); bge_stop(sc); BGE_UNLOCK(sc); @@ -6098,11 +6129,13 @@ bge_resume(device_t dev) sc = device_get_softc(dev); BGE_LOCK(sc); ifp = sc->bge_ifp; + bge_reset(sc); if (if_getflags(ifp) & IFF_UP) { bge_init_locked(sc); if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) bge_start_locked(ifp); } + bge_clrwol(sc); BGE_UNLOCK(sc); return (0); @@ -6847,3 +6880,73 @@ bge_debugnet_poll(if_t ifp, int count) return (0); } #endif /* DEBUGNET */ + +static void +bge_setwol(struct bge_softc *sc) +{ + struct ifnet *ifp; + uint32_t clk; + uint16_t pmstat; + int pmc; + +#ifdef BGE_WOL_DEBUG + device_printf(sc->bge_dev, "Entering bge WOL\n"); +#endif + ifp = sc->bge_ifp; + if ((if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) == 0) + return; + if (pci_find_cap(sc->bge_dev, PCIY_PMG, &pmc) != 0) + return; + if ((if_getcapenable(ifp) & IFCAP_WOL) != 0) { +#ifdef BGE_WOL_DEBUG + device_printf(sc->bge_dev, "Configuring bge WOL\n"); +#endif + bge_reset(sc); + BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_MAGIC_PKT_ENB); + BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_ACPI_PWRON_ENB); + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE); + BGE_SETBIT(sc, BGE_MAC_MODE, BGE_PORTMODE_GMII); + BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE); + clk = CSR_READ_4(sc, BGE_PCI_CLKCTL) | + BGE_PCICLOCKCTL_RXCPU_CLK_DIS | + BGE_PCICLOCKCTL_ALTCLK | + BGE_PCICLOCKCTL_SYSPLL_DISABLE; + CSR_WRITE_4(sc, BGE_PCI_CLKCTL, clk); + } + else { +#ifdef BGE_WOL_DEBUG + device_printf(sc->bge_dev, "Bypassing bge WOL\n"); +#endif + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_MAGIC_PKT_ENB); + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_ACPI_PWRON_ENB); + } + + /* Request PME if WOL is requested. */ + pmstat = pci_read_config(sc->bge_dev, pmc + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if ((if_getcapenable(ifp) & IFCAP_WOL) != 0) + pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->bge_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); +} + +static void +bge_clrwol(struct bge_softc *sc) +{ + struct ifnet *ifp; + uint16_t pmstat; + int pmc; + + ifp = sc->bge_ifp; + if ((if_getcapabilities(ifp) & IFCAP_WOL) == 0) + return; + if (pci_find_cap(sc->bge_dev, PCIY_PMG, &pmc) != 0) + return; + if ((if_getcapenable(ifp) & IFCAP_WOL) == 0) + return; + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_MAGIC_PKT_ENB); + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_ACPI_PWRON_ENB); + + pmstat = pci_read_config(sc->bge_dev, pmc + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + pci_write_config(sc->bge_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); +} diff --git a/sys/dev/bge/if_bgereg.h b/sys/dev/bge/if_bgereg.h index 7a56a202abd1..335dd012800f 100644 --- a/sys/dev/bge/if_bgereg.h +++ b/sys/dev/bge/if_bgereg.h @@ -456,7 +456,8 @@ #define BGE_PCICLOCKCTL_ALTCLK 0x00001000 #define BGE_PCICLOCKCTL_ALTCLK_SRC 0x00002000 #define BGE_PCICLOCKCTL_PCIPLL_DISABLE 0x00004000 -#define BGE_PCICLOCKCTL_SYSPLL_DISABLE 0x00008000 +#define BGE_PCICLOCKCTL_SYSPLL_DISABLE 0x00008000 /* Disable the 133 MHz + * phase-locked loop */ #define BGE_PCICLOCKCTL_BIST_ENABLE 0x00010000 #ifndef PCIM_CMD_MWIEN -- 2.43.2