Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 181fb18d authored by Vivek Natarajan's avatar Vivek Natarajan Committed by John W. Linville
Browse files

ath9k: Fix a PLL hang issue observed with AR9485.



When this PLL hang issue is seen, both Rx and Tx fail to work.
The sqsum_dvc needs to be below 2000 for a good chip. During
this issue the sqsum_dvc value is beyond 80000 and only a
full reset can solve this problem.

Signed-off-by: default avatarVivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent b1415819
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -641,6 +641,7 @@ struct ath_softc {
#endif
	struct ath_beacon_config cur_beacon_conf;
	struct delayed_work tx_complete_work;
	struct delayed_work hw_pll_work;
	struct ath_btcoex btcoex;

	struct ath_descdma txsdma;
+3 −0
Original line number Diff line number Diff line
@@ -230,6 +230,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
	cancel_work_sync(&sc->paprd_work);
	cancel_work_sync(&sc->hw_check_work);
	cancel_delayed_work_sync(&sc->tx_complete_work);
	cancel_delayed_work_sync(&sc->hw_pll_work);

	ath9k_ps_wakeup(sc);

@@ -290,6 +291,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
		if (sc->sc_flags & SC_OP_BEACONS)
			ath_beacon_config(sc, NULL);
		ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
		ath_start_ani(common);
	}

@@ -1263,6 +1265,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
		cancel_delayed_work_sync(&sc->ath_led_blink_work);

	cancel_delayed_work_sync(&sc->tx_complete_work);
	cancel_delayed_work_sync(&sc->hw_pll_work);
	cancel_work_sync(&sc->paprd_work);
	cancel_work_sync(&sc->hw_check_work);

+23 −0
Original line number Diff line number Diff line
@@ -2091,6 +2091,28 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
	}
}

static void ath_hw_pll_work(struct work_struct *work)
{
	struct ath_softc *sc = container_of(work, struct ath_softc,
					    hw_pll_work.work);
	static int count;

	if (AR_SREV_9485(sc->sc_ah)) {
		if (ar9003_get_pll_sqsum_dvc(sc->sc_ah) >= 0x40000) {
			count++;

			if (count == 3) {
				/* Rx is hung for more than 500ms. Reset it */
				ath_reset(sc, true);
				count = 0;
			}
		} else
			count = 0;

		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
	}
}

static void ath_tx_complete_poll_work(struct work_struct *work)
{
	struct ath_softc *sc = container_of(work, struct ath_softc,
@@ -2312,6 +2334,7 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
	}

	INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
	INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);

	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
		error = ath_tx_edma_init(sc);