Index: ah_osdep.c =================================================================== --- ah_osdep.c (revision 263418) +++ ah_osdep.c (working copy) @@ -138,6 +138,19 @@ #ifdef AH_DEBUG +static int +ath_hal_reg_whilst_asleep(struct ath_hal *ah, uint32_t reg) +{ + + if (reg >= 0x4000 && reg < 0x5000) + return (1); + if (reg >= 0x6000 && reg < 0x7000) + return (1); + if (reg >= 0x7000 && reg < 0x8000) + return (1); + return (0); +} + void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) { @@ -254,7 +267,8 @@ bus_space_handle_t h = ah->ah_sh; /* Debug - complain if we haven't fully waken things up */ - if (ah->ah_powerMode != HAL_PM_AWAKE) { + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", __func__, reg, val, ah->ah_powerMode); } @@ -285,7 +299,8 @@ u_int32_t val; /* Debug - complain if we haven't fully waken things up */ - if (ah->ah_powerMode != HAL_PM_AWAKE) { + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", __func__, reg, ah->ah_powerMode); } @@ -343,7 +358,8 @@ bus_space_handle_t h = ah->ah_sh; /* Debug - complain if we haven't fully waken things up */ - if (ah->ah_powerMode != HAL_PM_AWAKE) { + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", __func__, reg, val, ah->ah_powerMode); } @@ -363,7 +379,8 @@ u_int32_t val; /* Debug - complain if we haven't fully waken things up */ - if (ah->ah_powerMode != HAL_PM_AWAKE) { + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", __func__, reg, ah->ah_powerMode); } Index: if_ath.c =================================================================== --- if_ath.c (revision 263416) +++ if_ath.c (working copy) @@ -279,6 +279,88 @@ } } +/* + * Set the target power mode. + * + * If this is called during a point in time where + * the hardware is being programmed elsewhere, it will + * simply store it away and update it when all current + * uses of the hardware are completed. + */ +void +ath_power_setpower(struct ath_softc *sc, int power_state) +{ +// ATH_LOCK_ASSERT(sc); + + sc->sc_target_powerstate = power_state; + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: state=%d, refcnt=%d\n", + __func__, + power_state, + sc->sc_powersave_refcnt); + + if (sc->sc_powersave_refcnt == 0 && + power_state != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = power_state; + ath_hal_setpower(sc->sc_ah, power_state); + } +} + +/* + * Set the hardware power mode and take a reference. + * + * This doesn't update the target power mode in the driver; + * it just updates the hardware power state. + * + * XXX it should only ever force the hardware awake; it should + * never be called to set it asleep. + */ +void +ath_power_set_power_state(struct ath_softc *sc, int power_state) +{ +// ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: state=%d, refcnt=%d\n", + __func__, + power_state, + sc->sc_powersave_refcnt); + + sc->sc_powersave_refcnt++; + + if (power_state != sc->sc_cur_powerstate) { + ath_hal_setpower(sc->sc_ah, power_state); + sc->sc_cur_powerstate = power_state; + } +} + +/* + * Restore the power save mode to what it once was. + * + * This will decrement the reference counter and once it hits + * zero, it'll restore the powersave state. + */ +void +ath_power_restore_power_state(struct ath_softc *sc) +{ + +// ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: refcnt=%d\n", + __func__, + sc->sc_powersave_refcnt); + + if (sc->sc_powersave_refcnt == 0) + device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); + else + sc->sc_powersave_refcnt--; + + if (sc->sc_powersave_refcnt == 0 && + sc->sc_target_powerstate != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = sc->sc_target_powerstate; + ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); + } +} + #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ @@ -994,6 +1076,12 @@ if (bootverbose) ieee80211_announce(ic); ath_announce(sc); + + /* + * Put it to sleep for now. + */ + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + return 0; bad2: ath_tx_cleanup(sc); @@ -1039,7 +1127,14 @@ * it last * Other than that, it's straightforward... */ + + /* + * XXX Wake the hardware up first. ath_stop() will still + * wake it up first, but I'd rather do it here just to + * ensure it's awake. + */ ath_stop(ifp); + ieee80211_ifdetach(ifp->if_l2com); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG @@ -1600,6 +1695,10 @@ sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + + /* Ensure we set the current power state to on */ + ath_power_setpower(sc, HAL_PM_AWAKE); + ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, &status); @@ -1689,6 +1788,10 @@ return; } + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { HAL_INT status; @@ -1698,6 +1801,10 @@ ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return; } @@ -1737,6 +1844,11 @@ /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } @@ -1907,6 +2019,10 @@ ATH_PCU_LOCK(sc); sc->sc_intr_cnt--; ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void @@ -2042,6 +2158,11 @@ ATH_LOCK(sc); /* + * Force the sleep state awake. + */ + ath_power_setpower(sc, HAL_PM_AWAKE); + + /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ @@ -2058,6 +2179,7 @@ ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { if_printf(ifp, "unable to reset hardware; hal status %u\n", status); @@ -2170,6 +2292,12 @@ __func__, sc->sc_invalid, ifp->if_flags); ATH_LOCK_ASSERT(sc); + + /* + * Wake the hardware up before fiddling with it. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* * Shutdown the hardware and driver: @@ -2210,6 +2338,9 @@ sc->sc_rxlink = NULL; ath_beacon_free(sc); /* XXX not needed */ } + + /* And now, restore the current power state */ + ath_power_restore_power_state(sc); } #define MAX_TXRX_ITERATIONS 1000 @@ -2361,6 +2492,14 @@ /* Try to (stop any further TX/RX from occuring */ taskqueue_block(sc->sc_tq); + + /* + * Wake the hardware up. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); /* @@ -2739,6 +2878,11 @@ sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't @@ -2972,6 +3116,11 @@ sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + /* Sleep the hardware if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); @@ -3119,8 +3268,13 @@ __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); + /* Wake up the hardware first before updating the slot time */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); + ath_power_restore_power_state(sc); sc->sc_updateslot = OK; + ATH_UNLOCK(sc); } /* @@ -3137,6 +3291,8 @@ * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. + * + * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) @@ -4258,6 +4414,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); @@ -4278,6 +4438,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4299,6 +4463,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); @@ -4331,6 +4499,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4351,6 +4523,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* @@ -4376,6 +4552,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } #undef TXQACTIVE @@ -5057,6 +5237,11 @@ HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; + /* + * Force the hardware awake for ANI work. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); @@ -5086,6 +5271,7 @@ sc->sc_doresetcal = AH_TRUE; taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); + ath_power_restore_power_state(sc); return; } /* @@ -5157,6 +5343,10 @@ __func__); /* NB: don't rearm timer */ } + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); } static void @@ -5309,6 +5499,11 @@ */ IEEE80211_LOCK_ASSERT(ic); + /* Before we touch the hardware - wake it up */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; @@ -5322,6 +5517,12 @@ * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ + + /* Ensure we stay awake during scan */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); @@ -5480,7 +5681,15 @@ sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + /* + * Force awake for RUN mode. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). */ @@ -5491,6 +5700,7 @@ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -5510,9 +5720,22 @@ #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif + } else if (nstate == IEEE80211_S_SLEEP) { + /* We're going to sleep, so transition appropriately */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP); + ATH_UNLOCK(sc); } bad: ieee80211_free_node(ni); + + /* + * Restore the power state - either to what it was, or + * to network_sleep if it's alright. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return error; } @@ -5972,11 +6195,8 @@ } else { ATH_LOCK(sc); ath_stop_locked(ifp); -#ifdef notyet - /* XXX must wakeup in places like ath_vap_delete */ if (!sc->sc_invalid) - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); -#endif + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); ATH_UNLOCK(sc); } break; Index: if_ath_debug.h =================================================================== --- if_ath_debug.h (revision 263416) +++ if_ath_debug.h (working copy) @@ -68,6 +68,7 @@ ATH_DEBUG_SW_TX_FILT = 0x400000000ULL, /* SW TX FF */ ATH_DEBUG_NODE_PWRSAVE = 0x800000000ULL, /* node powersave */ ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */ + ATH_DEBUG_PWRSAVE = 0x2000000000ULL, ATH_DEBUG_ANY = 0xffffffffffffffffULL }; Index: if_ath_misc.h =================================================================== --- if_ath_misc.h (revision 263416) +++ if_ath_misc.h (working copy) @@ -128,6 +128,13 @@ extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq); /* + * Power state tracking. + */ +extern void ath_power_setpower(struct ath_softc *sc, int power_state); +extern void ath_power_set_power_state(struct ath_softc *sc, int power_state); +extern void ath_power_restore_power_state(struct ath_softc *sc); + +/* * Kick the frame TX task. */ static inline void Index: if_ath_rx.c =================================================================== --- if_ath_rx.c (revision 263416) +++ if_ath_rx.c (working copy) @@ -911,6 +911,10 @@ kickpcu = sc->sc_kickpcu; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); ngood = 0; nf = ath_hal_getchannoise(ah, sc->sc_curchan); @@ -1066,6 +1070,13 @@ #undef PA2DESC /* + * Put the hardware to sleep again if we're done with it. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* * If we hit the maximum number of frames in this round, * reschedule for another immediate pass. This gives * the TX and TX completion routines time to run, which Index: if_ath_tx.c =================================================================== --- if_ath_tx.c (revision 263416) +++ if_ath_tx.c (working copy) @@ -2106,6 +2106,7 @@ int do_override; uint8_t type, subtype; int queue_to_head; + struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); @@ -2183,12 +2184,24 @@ rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + /* Fetch first rate information */ rix = ath_tx_findrix(sc, params->ibp_rate0); + try0 = params->ibp_try0; + + /* + * Override EAPOL rate as appropriate. + */ + if (m0->m_flags & M_EAPOL) { + /* XXX? maybe always use long preamble? */ + rix = an->an_mgmtrix; + try0 = ATH_TXMAXTRY; /* XXX?too many? */ + } + txrate = rt->info[rix].rateCode; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) txrate |= rt->info[rix].shortPreamble; sc->sc_txrix = rix; - try0 = params->ibp_try0; ismrr = (params->ibp_try1 != 0); txantenna = params->ibp_pri >> 2; if (txantenna == 0) /* XXX? */ @@ -2261,8 +2274,7 @@ /* Blank the legacy rate array */ bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); - bf->bf_state.bfs_rc[0].rix = - ath_tx_findrix(sc, params->ibp_rate0); + bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].tries = try0; bf->bf_state.bfs_rc[0].ratecode = txrate; @@ -2359,6 +2371,11 @@ sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_TX_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { @@ -2437,7 +2454,14 @@ sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return 0; + bad2: ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " "bf=%p", @@ -2447,14 +2471,20 @@ ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); + bad: - ATH_TX_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + bad0: + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); ifp->if_oerrors++; Index: if_athvar.h =================================================================== --- if_athvar.h (revision 263416) +++ if_athvar.h (working copy) @@ -864,6 +864,13 @@ void (*sc_bar_response)(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); + + /* + * powersave state tracking. + */ + HAL_POWER_MODE sc_target_powerstate; + HAL_POWER_MODE sc_cur_powerstate; + int sc_powersave_refcnt; }; #define ATH_LOCK_INIT(_sc) \