diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c --- 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_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 *); @@ -888,6 +890,7 @@ 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) @@ -920,8 +923,24 @@ 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 @@ 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 @@ #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 @@ 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 @@ 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 @@ /* 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) @@ -5841,6 +5867,9 @@ } } #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); @@ -6068,6 +6097,7 @@ sc = device_get_softc(dev); BGE_LOCK(sc); + bge_setwol(sc); bge_stop(sc); BGE_UNLOCK(sc); @@ -6081,6 +6111,7 @@ sc = device_get_softc(dev); BGE_LOCK(sc); + bge_setwol(sc); bge_stop(sc); BGE_UNLOCK(sc); @@ -6096,11 +6127,13 @@ 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); @@ -6850,3 +6883,73 @@ 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 --- 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