39:17 GMT ETag: "2343f56e64987730bcb3ad1cb5542c41dcd94310" /* * Copyright (c) 2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "ath9k.h" /* * TX polling - checks if the TX engine is stuck somewhere * and issues a chip reset if so. */ void ath_tx_complete_poll_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, tx_complete_work.work); struct ath_txq *txq; int i; bool needreset = false; if (sc->tx99_state) { ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, "skip tx hung detection on tx99\n"); return; } for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; ath_txq_lock(sc, txq); if (txq->axq_depth) { if (txq->axq_tx_inprogress) { needreset = true; ath_txq_unlock(sc, txq); break; } else { txq->axq_tx_inprogress = true; } } ath_txq_unlock(sc, txq); } if (needreset) { ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, "tx hung, resetting the chip\n"); ath9k_queue_reset(sc, RESET_TYPE_TX_HANG); return; } ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); } /* * Checks if the BB/MAC is hung. */ bool ath_hw_check(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum ath_reset_type type; bool is_alive; ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); if (!is_alive) { ath_dbg(common, RESET, "HW hang detected, schedule chip reset\n"); type = RESET_TYPE_MAC_HANG; ath9k_queue_reset(sc, type); } ath9k_ps_restore(sc); return is_alive; } /* * PLL-WAR for AR9485/AR9340 */ static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) { static int count; struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (pll_sqsum >= 0x40000) { count++; if (count == 3) { ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG); count = 0; return true; } } else { count = 0; } return false; } void ath_hw_pll_work(struct work_struct *work) { u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); struct ath_common *common = ath9k_hw_common(sc->sc_ah); /* * ensure that the PLL WAR is executed only * after the STA is associated (or) if the * beaconing had started in interfaces that * uses beacons. */ if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) return; if (sc->tx99_state) return; ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) return; ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); } /* * PA Pre-distortion. */ static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; if (!caldata || !t